{
    "version": "https://jsonfeed.org/version/1.1",
    "title": "Blog | Brandon Rozek",
    "home_page_url": "https://brandonrozek.com/",
    "feed_url": "https://brandonrozek.com/blog/index.json",
    "description": " Brandon Rozek is a Computer Scientist often writing about topics surrounding Linux and Software Development. ",
    "icon": "https://brandonrozek.com/img/avatar.jpg",
    "language": "en-US",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "items": [
        
            {
    "id": "https://brandonrozek.com/blog/post-quantum-security-adoption/",
    "url": "https://brandonrozek.com/blog/post-quantum-security-adoption/",
    "title": "On Post-Quantum Security Adoption",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eFrom Alex\u0026rsquo;s \u003ca href=\"https://alexwlchan.net/2026/post-quantum-blog/\"\u003eblog post\u003c/a\u003e, I\u0026rsquo;ve learned that there are enough recent breakthroughs in quantum computing that I should take post-quantum cryptography seriously. \u003ca href=\"https://blog.google/innovation-and-ai/technology/safety-security/cryptography-migration-timeline/\"\u003eGoogle\u003c/a\u003e and \u003ca href=\"https://blog.cloudflare.com/post-quantum-roadmap/\"\u003eCloudflare\u003c/a\u003e both set a target of 2029 for having their systems secure against quantum computers. Similarly, the \u003ca href=\"https://www.ncsc.gov.uk/guidance/pqc-migration-timelines\"\u003eUK government\u003c/a\u003e is targeting 2035.\u003c/p\u003e\n\u003cp\u003eThe issue is that cryptography is built upon math problems that are difficult to solve. Quantum computers make solving some of these problems such as integer factorization and discrete logs easier. If someone has a quantum computer that can sufficiently solve those two problems, then they can likely decrypt many ciphertexts that were produced using \u003ca href=\"https://en.wikipedia.org/wiki/Public-key_cryptography\"\u003easymmetric cryptography\u003c/a\u003e techniques (think public/private key-pairs). Wikipedia has a great article discussing \u003ca href=\"https://en.wikipedia.org/wiki/Post-quantum_cryptography\"\u003epost-quantum cryptography\u003c/a\u003e if you want to read more.\u003c/p\u003e\n\u003cp\u003eGiven all that, if the cost isn\u0026rsquo;t too high then it\u0026rsquo;s not a bad idea to look at our current systems and see what we can make quantum-resistant today. Otherwise, we risk being vulnerable to \u003ca href=\"https://en.wikipedia.org/wiki/Harvest_now,_decrypt_later\"\u003eharvest now, decrypt later\u003c/a\u003e attacks. This is where an adversary stores encrypted packets until they have a computer powerful enough to break the encryption. You might wonder if encrypted packets from 5 years ago matter. Though if you\u0026rsquo;re like me, chances are you had to transmit personally identifiable information over the internet for jobs, housing, etc. In the US, it is \u003ca href=\"https://www.ssa.gov/faqs/en/questions/KA-02220.html\"\u003ereally difficult\u003c/a\u003e to change your social security number.\u003c/p\u003e\n\u003cp\u003eWe\u0026rsquo;ll explore three different protocols I rely on and how we can make them post-quantum resistant.\u003c/p\u003e\n\u003ch2 id=\"ssh\"\u003eSSH\u003c/h2\u003e\n\u003cp\u003eOpenSSH implemented and made default post-quantum key agreement \u003ca href=\"https://www.openssh.org/pq.html\"\u003eback in April 2022\u003c/a\u003e. At the time of writing, the \u003ccode\u003emlkem768x25519-sha256\u003c/code\u003e scheme is used. That\u0026rsquo;s a mouthful but it essentially describes what the scheme is:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003emlkem\u003c/code\u003e: \u003ca href=\"https://en.wikipedia.org/wiki/Lattice-based_cryptography\"\u003eModule lattices\u003c/a\u003e \u003ca href=\"https://en.wikipedia.org/wiki/Key_encapsulation_mechanism\"\u003ekey encapsulation mechanism\u003c/a\u003e (also known as key-exchange)\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e768\u003c/code\u003e: Not sure what this means, sorry.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ex25519\u003c/code\u003e: \u003ca href=\"https://en.wikipedia.org/wiki/Curve25519\"\u003eElliptic curve\u003c/a\u003e used in the classical Diffie-Hellman key-exchange.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003esha256\u003c/code\u003e: The hash function used to \u003ca href=\"https://datatracker.ietf.org/doc/draft-ietf-sshm-mlkem-hybrid-kex/\"\u003ecombine the keys\u003c/a\u003e generated from ML-KEM and x25519 (see Section 2.4 of previous link).\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eAs you might notice, this is a hybrid quantum/classical algorithm. This is a hedge. If quantum computers arrive which break the classical algorithm, then we\u0026rsquo;re safe. Similarly if we find out that the post-quantum algorithms are insecure, then we still have the well-developed classical ones. We can only hope that they\u0026rsquo;re both not found to be insecure.\u003c/p\u003e\n\u003cp\u003eIf you are using a client released after October 2025, then you\u0026rsquo;ll receive a warning if you\u0026rsquo;re connecting to a server that doesn\u0026rsquo;t support post-quantum encryption.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e** WARNING: connection is not using a post-quantum key exchange algorithm.\n** This session may be vulnerable to \u0026#34;store now, decrypt later\u0026#34; attacks.\n** The server may need to be upgraded. See https://openssh.com/pq.html\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"tls\"\u003eTLS\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://en.wikipedia.org/wiki/Transport_Layer_Security\"\u003eTransport Layer Security\u003c/a\u003e is a cryptographic protocol that underlies HTTPS and many other applications. You\u0026rsquo;re likely using it to connect to this website! Similar to OpenSSH, they introduced the hybrid scheme \u003ccode\u003eX25519MLKEM768\u003c/code\u003e for post-quantum security. The main difference that I can spot (as a non-cryptographer) is that this scheme does not hash the combined key. If you\u0026rsquo;re interested, you can read more at this \u003ca href=\"https://datatracker.ietf.org/doc/draft-ietf-tls-ecdhe-mlkem/\"\u003eIETF draft\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eIn terms of major browsers, Firefox has supported this since \u003ca href=\"https://www.firefox.com/en-US/firefox/132.0/releasenotes/\"\u003eOctober 2024\u003c/a\u003e and Chrome since \u003ca href=\"https://developer.chrome.com/release-notes/131\"\u003eNovember 2024\u003c/a\u003e. To use this scheme, however, both sides need to handle it. At the time of writing, \u003ca href=\"https://radar.cloudflare.com/post-quantum\"\u003eCloudflare\u003c/a\u003e estimates that 70.1% of Internet traffic is post-quantum encrypted. You can even use that link to check whether your own website supports post-quantum key exchange.\u003c/p\u003e\n\u003cp\u003eIf you find that your website is not post-quantum secure, then check out the \u003ca href=\"https://configurator.tlsref.org/\"\u003eTLSRef TLS Configurator\u003c/a\u003e. This provides the configuration options needed to support modern cryptographic protocols for a variety of popular web servers.\u003c/p\u003e\n\u003ch2 id=\"wireguard\"\u003eWireGuard\u003c/h2\u003e\n\u003cp\u003eWireGuard is a popular VPN technology that is known for being efficient and simple. By default, it is \u003ca href=\"https://www.wireguard.com/known-limitations/\"\u003enot post-quantum secure\u003c/a\u003e. However, we can use the \u003ccode\u003ePresharedKey\u003c/code\u003e field to mix a symmetric key whose underlying mathematical problems are not as impacted as asymmetric cryptography.\u003c/p\u003e\n\u003cp\u003eThe simplest solution here then is to run\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ewg genpsk\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eand then \u003cem\u003esecurely\u003c/em\u003e add the key into the \u003ccode\u003ePresharedKey\u003c/code\u003e field.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Interface]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAddress\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePrivateKey\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Peer]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePublicKey\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePresharedKey\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBoth sides of the connection need to use the same \u003ccode\u003ePresharedKey\u003c/code\u003e in order for the connection to work.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ll reiterate that we need to figure out a way to transfer the key \u003cem\u003esecurely\u003c/em\u003e for this to work. WireGuard doesn\u0026rsquo;t have a built-in way to share these keys. If you want to go with this simpler static preshared key route, then I suggest that you use a post-quantum secure channel (like a modern SSH setup) to distribute the keys.\u003c/p\u003e\n\u003cp\u003eThe downside of setting the preshared key on both sides once and then moving on is that the tunnel is then not post-quantum forward secret. This means that if an adversary with access to a sufficiently powerful quantum computer additionally gets access to the preshared key, they can then decrypt all future ciphertexts.\u003c/p\u003e\n\u003cp\u003eIf we want to protect against this, then we\u0026rsquo;ll need to run a post-quantum key exchange protocol on top of WireGuard. At the time of writing, \u003ca href=\"https://github.com/rosenpass/rosenpass\"\u003eRosenpass\u003c/a\u003e seems to be the simplest way to set this up. They automatically update the preshared key securely within WireGuard every two minutes.\u003c/p\u003e\n\u003cp\u003eWhile that is one solution, the space here seems fragmented. Another approach \u003ca href=\"https://ieeexplore.ieee.org/abstract/document/9519445\"\u003eupdates the protocol itself\u003c/a\u003e and many VPN providers \u003ca href=\"https://prod-assets-cms.mtech.xvservice.net/files/xv/Post-Quantum-WireGuard_-A-Practical-Implementation-Guide.pdf\"\u003ehandle it their own way as well\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eEven though we\u0026rsquo;re not at \u003ca href=\"https://en.wikipedia.org/wiki/Harvest_now,_decrypt_later\"\u003eY2Q or Q-day\u003c/a\u003e, we can still take steps to make sure that we\u0026rsquo;re transmitting information over the Internet in a quantum-resistant way. In the meantime, I\u0026rsquo;ll dream about the day when I can run a quantum computer in my pocket.\u003c/p\u003e\n",
    "date_published": "2026.06.15",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/synchronizing-static-website-object-storage/",
    "url": "https://brandonrozek.com/blog/synchronizing-static-website-object-storage/",
    "title": "Synchronizing my Static Website with Object Storage",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI recently updated all my \u003ca href=\"/blog/implementing-cdn-geodns/\"\u003egeo-distributed\u003c/a\u003e web servers to run on Fedora CoreOS (\u003ca href=\"/blog/fedora-coreos-first-impressions/\"\u003eyes, I still love it\u003c/a\u003e). This gave me an opportunity to revisit how I handle synchronization. Before, I used \u003ca href=\"https://syncthing.net/\"\u003eSyncthing\u003c/a\u003e which while awesome is a pain to configure. I don\u0026rsquo;t update my website or certs too frequently so having an always online setup seemed overkill.\u003c/p\u003e\n\u003cp\u003eSo this time I went with an object storage setup.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/website-object-store.svg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eI created a bucket (e.g \u003ccode\u003emy-website\u003c/code\u003e) and within it I have the following directories\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003emy-website\n├── etc\n    └── letsencrypt\n        └── live\n            └── example.com\n                ├── cert.pem\n                ├── chain.pem\n                ├── fullchain.pem\n                └── privkey.pem\n└── var\n    └── www\n        ├── website1\n        ├── website2\n        └── websiten\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe core idea is that the webservers will read from this object store to stay up to date with my SSL certificates and my static website files. The rest of the post will go over how I 1) modified my deployment pipeline to push to the object store, 2) push the SSL certificates which are renewed, and 3) pull both the SSL certificates and the website files.\u003c/p\u003e\n\u003ch2 id=\"deploying-my-website-files\"\u003eDeploying my website files\u003c/h2\u003e\n\u003cp\u003eCurrently, I use \u003ca href=\"https://brandonrozek.com/blog/deploying-hugo-website-through-gh-actions/\"\u003eGithub Actions\u003c/a\u003e (labeled as \u003ccode\u003eCI/CD\u003c/code\u003e in the diagram) to build and deploy my website. Beforehand, I had to create a special SSH key-pair and lock it down in case it leaked. For this new setup, we can instead use application keys to authenticate with our object store.\u003c/p\u003e\n\u003cp\u003eTo lower the threat surface further, we can limit the buckets the application key has access to, whether it has read/write permissions, and what file prefixes the application can access.\u003c/p\u003e\n\u003cp\u003eFor my Github action, I created an application key which has read/write permissions to the prefix \u003ccode\u003evar/www/website\u003c/code\u003e. We need both permissions if we want to delete files that don\u0026rsquo;t exist in the build anymore. Here\u0026rsquo;s the script that I use within the GitHub action to synchronize with the object store after I built the website.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/usr/bin/env sh\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003eset -e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Environmental variables we need to set within the runner\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eAWS_ACCESS_KEY_ID:?AWS_ACCESS_KEY_ID is not set\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eAWS_SECRET_ACCESS_KEY:?AWS_SECRET_ACCESS_KEY is not set\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eAWS_ENDPOINT_URL:?AWS_ENDPOINT_URL is not set\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eS3_BUCKET:?S3_BUCKET is not set\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eS3_PATH:?S3_PATH is not set\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Safety check so we don\u0026#39;t wipe our website!\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e ! -d \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;public\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e -z \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003els -A public\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;public/ is empty or missing\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eaws s3 sync public/ \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s3://\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eS3_BUCKET\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eS3_PATH\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --delete \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --exclude \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;*.bak\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSummarizing how the script works:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThe environmental variables at the beginning are used to authenticate with the object store.\u003c/li\u003e\n\u003cli\u003eWe sanity check that the \u003ccode\u003epublic\u003c/code\u003e folder exists after running \u003ccode\u003ehugo\u003c/code\u003e and that it\u0026rsquo;s non-empty so that we don\u0026rsquo;t accidentally wipe the object store\u0026rsquo;s files.\u003c/li\u003e\n\u003cli\u003eWe use the \u003ccode\u003eaws\u003c/code\u003e command to perform the sync. Note that this command is \u003ca href=\"https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md\"\u003ebaked into\u003c/a\u003e the default Ubuntu image used by GitHub\u0026rsquo;s runners, so no installation step is required.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"pushing-ssl-certificates\"\u003ePushing SSL Certificates\u003c/h2\u003e\n\u003cp\u003eI use \u003ca href=\"https://certbot.eff.org/\"\u003eCertbot\u003c/a\u003e to request SSL certificates from Let\u0026rsquo;s Encrypt. After a renewal certificate is issued, the client will run any scripts located within \u003ccode\u003e/etc/letsencrypt/renewal-hooks/deploy\u003c/code\u003e (\u003ca href=\"https://eff-certbot.readthedocs.io/en/stable/using.html#renewing-certificates\"\u003edocumentation\u003c/a\u003e). In that case, we want to add a \u003ccode\u003epush-certs-to-object-store.sh\u003c/code\u003e file which does just that.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003eset -u\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eS3_BUCKET\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;INSERT_BUCKET_NAME\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eENDPOINT\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;INSERT_ENDPOINT_URL\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAWS_ACCESS_KEY_ID\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;INSERT_KEY_ID_HERE\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAWS_SECRET_ACCESS_KEY\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;INSERT_SECRET_KEY_HERE\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epodman run --rm \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  -e AWS_ACCESS_KEY_ID\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eAWS_ACCESS_KEY_ID\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  -e AWS_SECRET_ACCESS_KEY\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eAWS_SECRET_ACCESS_KEY\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  -v \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eRENEWED_LINEAGE\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e:\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eRENEWED_LINEAGE\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e:ro,z\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  -v \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/etc/letsencrypt/archive:/etc/letsencrypt/archive:ro,z\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  docker.io/amazon/aws-cli s3 cp \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eRENEWED_LINEAGE\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s3://\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eS3_BUCKET\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eRENEWED_LINEAGE#/\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --recursive \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --endpoint-url \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eENDPOINT\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e $? -ne \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e# Insert failure notification technique here\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eMake sure that this script is executable after saving it. This script is different from the last in that it uses \u003ccode\u003epodman\u003c/code\u003e to run \u003ccode\u003eaws-cli\u003c/code\u003e as opposed to executing it directly. This is because I\u0026rsquo;m using Fedora CoreOS an immutable distribution which \u003ca href=\"https://docs.fedoraproject.org/en-US/fedora-coreos/faq/#_how_do_i_run_custom_applications_on_fedora_coreos\"\u003ehighly discourages overlaying packages\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eGoing over the script:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003e${RENEWED_LINEAGE}\u003c/code\u003e is the folder path which contains the renewed certs (e.g \u003ccode\u003e/etc/letsencrypt/live/example.com\u003c/code\u003e)\u003c/li\u003e\n\u003cli\u003eWe need to mount the \u003ccode\u003e${RENEWED_LINEAGE}\u003c/code\u003e path as well as \u003ccode\u003e/etc/letsencrypt/archive\u003c/code\u003e since the live folder only contains symbolic links to the files which are actually stored in the archive.\u003c/li\u003e\n\u003cli\u003eSince we\u0026rsquo;re not modifying these files, we can treat them as read-only (the \u003ccode\u003ero\u003c/code\u003e flag). Since I have SELinux enabled I threw in the \u003ccode\u003ez\u003c/code\u003e flag so that Podman can automatically handle the contexts for me.\u003c/li\u003e\n\u003cli\u003eWe\u0026rsquo;re using \u003ccode\u003ecp\u003c/code\u003e instead of \u003ccode\u003esync\u003c/code\u003e since the renewal procedure will overwrite all the existing files with new ones. Meaning that we don\u0026rsquo;t need read permissions for this application key to update the certificates.\u003c/li\u003e\n\u003cli\u003ePersonally for alerting, I send a curl request to a webhook on failure.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"pulling-the-files\"\u003ePulling the files\u003c/h2\u003e\n\u003cp\u003eWe can create an application key with read-only permissions in order to pull the SSL certificates and the website files.\u003c/p\u003e\n\u003ch3 id=\"website-files\"\u003eWebsite Files\u003c/h3\u003e\n\u003cp\u003eHere\u0026rsquo;s the script that I use to synchronize the local copy with that of the object store:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003eset -e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eAWS_ACCESS_KEY_ID:?AWS_ACCESS_KEY_ID is not set\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eAWS_SECRET_ACCESS_KEY:?AWS_SECRET_ACCESS_KEY is not set\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eAWS_ENDPOINT_URL:?AWS_ENDPOINT_URL is not set\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eS3_BUCKET:?S3_BUCKET is not set\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esync_site\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local path\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    mkdir -p \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003epath\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    podman run --rm \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e      -e AWS_ACCESS_KEY_ID\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eAWS_ACCESS_KEY_ID\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e      -e AWS_SECRET_ACCESS_KEY\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eAWS_SECRET_ACCESS_KEY\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e      -v \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003epath\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e:/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003epath\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e:z\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e      docker.io/amazon/aws-cli s3 sync \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e      \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s3://\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eS3_BUCKET\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003epath\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003epath\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e      --delete \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e      --endpoint-url \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eAWS_ENDPOINT_URL\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esync_site \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;var/www/website1\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esync_site \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;var/www/website2\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esync_site \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;var/www/websiten\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI have a corresponding systemd unit file and timer which runs every 15 minutes to check for changes.\u003c/p\u003e\n\u003ch3 id=\"ssl-certificates\"\u003eSSL Certificates\u003c/h3\u003e\n\u003cp\u003eFor my certificates, I only check for new ones daily. The script is very similar\u0026hellip;\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003eset -e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eAWS_ACCESS_KEY_ID:?AWS_ACCESS_KEY_ID is not set\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eAWS_SECRET_ACCESS_KEY:?AWS_SECRET_ACCESS_KEY is not set\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eAWS_ENDPOINT_URL:?AWS_ENDPOINT_URL is not set\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eS3_BUCKET:?S3_BUCKET is not set\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eS3_PATH:?S3_PATH is not set\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir -p -m \u003cspan style=\"color:#ae81ff\"\u003e700\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eS3_PATH\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epodman run --rm \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  -e AWS_ACCESS_KEY_ID\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eAWS_ACCESS_KEY_ID\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  -e AWS_SECRET_ACCESS_KEY\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eAWS_SECRET_ACCESS_KEY\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  -v \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eS3_PATH\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u0026#34;\u003c/span\u003e:\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eS3_PATH\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u0026#34;\u003c/span\u003e:z \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  docker.io/amazon/aws-cli s3 cp \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s3://\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eS3_BUCKET\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eS3_PATH\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eS3_PATH\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --recursive \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --endpoint-url \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eAWS_ENDPOINT_URL\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eThat\u0026rsquo;s at least how I have it set up at the time of writing. I\u0026rsquo;ve only been running this setup for two days, so we\u0026rsquo;ll see how I feel ultimately. Right now, I\u0026rsquo;m happy that it simplifies my Ansible setup for these servers. Before this, I had to manually setup Syncthing using their webui.\u003c/p\u003e\n\u003cp\u003eThis updated method allows me to copy a few scripts and systemd unit files over and call it a day. I\u0026rsquo;m also happy that I don\u0026rsquo;t have to deal with creating a special \u003ccode\u003ebuild\u003c/code\u003e user and making sure that\u0026rsquo;s locked down. Now we\u0026rsquo;ll see if I can be patient for 15 minutes to see my website changes ;D\u003c/p\u003e\n",
    "date_published": "2026.06.13",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/discrimination-testing/",
    "url": "https://brandonrozek.com/blog/discrimination-testing/",
    "title": "Can you tell the difference? A quick look into discrimination testing.",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eA few month\u0026rsquo;s ago, Brad Reese, the grandson of the founder of the Reese\u0026rsquo;s company, called the company out for \u003ca href=\"https://www.linkedin.com/posts/bradreesecom_reeses-brandstewardship-corporateaccountability-activity-7428545969430016001-zOL7/\"\u003eswapping out the chocolate and peanut butter in some of their products\u003c/a\u003e. The newer ingredients \u0026ldquo;chocolate candy\u0026rdquo; and \u0026ldquo;peanut butter creme\u0026rdquo; are only imitations.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eBut today, REESE\u0026rsquo;S  identity is being rewritten, not by storytellers, but by formulation  decisions that replace Milk Chocolate with compound coatings and Peanut  Butter with peanut‑butter‑style crèmes across multiple REESE’S products.\u003c/p\u003e\n\u003cp\u003e- Brad Reese\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eJonathan Deutsch over at the Conversation wrote that product reformulations in foods are common and most of the time \u003ca href=\"https://theconversation.com/controversy-over-reeses-ingredients-reveals-standard-food-industry-practices-most-consumers-never-notice-276808\"\u003ewe don\u0026rsquo;t even notice\u003c/a\u003e. This along with shrinking the size of the product are the two main approaches companies use to reduce costs.\u003c/p\u003e\n\u003ch2 id=\"discrimination-testing-for-food\"\u003eDiscrimination Testing for Food\u003c/h2\u003e\n\u003cp\u003eSo how can companies be confident that few will notice? That\u0026rsquo;s where discrimination testing comes in. From my research and what Jonathan shared, the most common type of discrimination test used in the food science industry is the \u003ca href=\"https://www.sensorysociety.org/knowledge/sspwiki/Pages/Triangle%20Test.aspx\"\u003etriangle test\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eIn this test, the participant is given three products (A, B, C) and are told that only one of them is different. If the participant is able to correctly identify which one, then there\u0026rsquo;s evidence that the average person can do the same. The probability that a random guess is correct is 1/3. Therefore, it\u0026rsquo;s not sufficient to ask a single person and instead we need many participants to obtain statistical significance.\u003c/p\u003e\n\u003ch2 id=\"discrimination-testing-for-audio\"\u003eDiscrimination Testing for Audio\u003c/h2\u003e\n\u003cp\u003eA few weeks later, I read Andreas\u0026rsquo; post on \u003ca href=\"https://82mhz.net/posts/2026/03/can-i-hear-a-difference-between-mp3s-and-uncompressed-audio/\"\u003etelling the difference between compressed and uncompressed audio\u003c/a\u003e. This made me curious on what discrimination tests are commonly used in the audio setting. However, this was difficult for me to search for.\u003c/p\u003e\n\u003cp\u003eIn the food setting, I\u0026rsquo;ve found multiple presentations and publications by the \u003ca href=\"https://www.sensorysociety.org/Pages/default.aspx\"\u003eSociety of Sensory Professionals\u003c/a\u003e and the \u003ca href=\"https://www.ifpress.com/\"\u003eInstitute for Perception\u003c/a\u003e. I wasn\u0026rsquo;t able to easily find any groups focused on the audio setting. As our last resort, we\u0026rsquo;ll trust Wikipedia and say that \u003ca href=\"https://en.wikipedia.org/wiki/ABX_test\"\u003eABX Testing\u003c/a\u003e is commonly used.\u003c/p\u003e\n\u003cp\u003eIn ABX testing, the participant is told which sample is A and which sample is B. The task is then to guess whether X = A or X = B. What\u0026rsquo;s important here is that the participant knows that A is not equal to B. Hence, the participant is provided with more information than in the triangle test setting.\u003c/p\u003e\n\u003cp\u003eFor our audio example, we can say that A is the uncompressed audio, B is the compressed audio, and the task is to guess which X is. The probability of a random guess being correct in this test is 1/2 which is much higher than the triangle test.\u003c/p\u003e\n\u003cp\u003eHonestly, I can\u0026rsquo;t figure out why this test is more common than the others. If you\u0026rsquo;re a perception researcher and have some insight, please get in touch.\u003c/p\u003e\n\u003cp\u003eOne thing to note about discrimination testing is that it only tells us if there is a perceptible difference. When comparing compression algorithms, we often want feedback on the perceived quality differences. I didn\u0026rsquo;t look into this area much, but if you\u0026rsquo;re interested then the \u003ca href=\"https://en.wikipedia.org/wiki/MUSHRA\"\u003eMUSHRA\u003c/a\u003e method seems to be a good starting point.\u003c/p\u003e\n\u003ch2 id=\"the-tip-of-the-iceburg\"\u003eThe Tip of the Iceburg\u003c/h2\u003e\n\u003cp\u003eAs you might have noticed by now, I\u0026rsquo;m not a perception researcher. There are a lot of discrimination tests out there and many folks are working on new ideas in this space. To close out this quick introduction,  I\u0026rsquo;ll share two other approaches that I came across when writing this post.\u003c/p\u003e\n\u003cp\u003eFirst, there\u0026rsquo;s the \u003ca href=\"https://www.sensorysociety.org/knowledge/sspwiki/Pages/Two-out-of-five%20Test.aspx\"\u003etwo out of five test\u003c/a\u003e. It\u0026rsquo;s similar to the triangle test where the participant is given no labels but this time they\u0026rsquo;re asked to identify the two that are unlike the other three. The probability of randomly guessing correctly in this test is 1/10 which is by far the lowest out of the approaches I shared in this post. The downside to this test is that the participant has to sample five items which might cause sensory fatigue and it\u0026rsquo;s easy to give \u003ca href=\"https://onlinelibrary.wiley.com/doi/10.1111/joss.12044\"\u003eincorrect instructions\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.ifpress.com/tr-15-1\"\u003eTetrad testing\u003c/a\u003e is when the participant is given 4 samples and are asked to pair up the equivalent ones. The probability of guessing correctly is equivalent to that of the triangle test, but Ennis and Jesionka show that in some cases the Tetrad test \u003ca href=\"https://doi.org/10.1111/j.1745-459X.2011.00353.x\"\u003erequires one third the number of participants as that required by the triangle test\u003c/a\u003e. The Institute for Perception released a techincal report arguing for the switch to Tetrad testing in order to \u003ca href=\"https://www.ifpress.com/tr-15-1\"\u003ereduce costs\u003c/a\u003e. There\u0026rsquo;s even a fun presentation publicly available by Hannah Lemar showing the suitability of the Tetrad test in the \u003ca href=\"https://www.asbcnet.org/events/archives/2016/proceedings/Documents/8_Lemar.pdf\"\u003ebrewing industry\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eWhat do you think, is it important to perceive the original, or does an imitation work for you? For me, I don\u0026rsquo;t mind listening to compressed audio at all. I like to think, however, that I\u0026rsquo;m aware of what I\u0026rsquo;m eating.\u003c/p\u003e\n",
    "date_published": "2026.05.24",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/praise-smart-button/",
    "url": "https://brandonrozek.com/blog/praise-smart-button/",
    "title": "Praise the Smart Button",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\n\u003cimg alt=\"Lamp which has a giraffe as it's base\" height=\"500px\" src=\"/files/images/blog/202605222012.jpg\" /\u003e\n\u003cbr/\u003e\n\n\u003cp\u003eMeet Giraffe. Equipped with an \u003ca href=\"https://www.zigbee2mqtt.io/devices/AE_270_T.html\"\u003eInnr AE 270 T\u003c/a\u003e smart bulb, it allows me to turn the light on and off from our phones using \u003ca href=\"https://www.home-assistant.io/\"\u003eHome Assistant\u003c/a\u003e. The main issue? I need my phone to turn it on and off.\u003c/p\u003e\n\u003cp\u003eNow don\u0026rsquo;t get me wrong, I can still walk up to the lamp and flip the switch. But, then I need to walk back up to it in order to turn it back on. Turns out, the smart bulb needs some power to receive control messages via the network.\u003c/p\u003e\n\u003cp\u003eIf that wasn\u0026rsquo;t annoying enough, flipping the switch off would mess with my cool automations. At sunrise, it\u0026rsquo;s supposed to turn on to help wake me up and then automatically turn itself off after noon.\u003c/p\u003e\n\u003cp\u003eSo I lived with that reality for a few years. Whenever I want to turn on or off the lamp, I would pull out my phone. Forgot to turn off the lights when I left the house? No worries, I can still control it remotely.\u003c/p\u003e\n\u003cp\u003eBut that all changed this Christmas when my wife gifted me the following\u0026hellip;\u003c/p\u003e\n\n\u003cimg alt=\"Lamp which has a giraffe as it's base\" height=\"500px\" src=\"/files/images/blog/202605222013.jpg\" /\u003e\n\u003cbr/\u003e\n\n\u003cp\u003eThat\u0026rsquo;s right. It\u0026rsquo;s a \u003ca href=\"https://www.zigbee2mqtt.io/devices/3RSB22BZ.html\"\u003eThird Reality 3RSB22BZ\u003c/a\u003e smart button. Setting this up was relatively straightforward. On Home Assistant, I clicked on \u0026ldquo;Add Device\u0026rdquo; and then specified that I wanted to add a Zigbee device. This requires a Zigbee hub, and for that I use the \u003ca href=\"https://www.home-assistant.io/connect/zbt-2/\"\u003eHome Assistant Connect ZBT-2\u003c/a\u003e. After that, I went through a short pairing process and tada the device is added!\u003c/p\u003e\n\u003cp\u003eBy itself, the button doesn\u0026rsquo;t actually do anything. To change that, we need to set up automations. At the time of writing, Home Assistant specifies automations through triggers, conditions, and actions. I set my trigger to \u003ccode\u003eremote_button_short_press\u003c/code\u003e. I didn\u0026rsquo;t add any extra conditions, and my action is to toggle my Giraffe lamp.\u003c/p\u003e\n\u003cp\u003eWith that, I don\u0026rsquo;t need to pull out my phone anymore to control the light!\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eI\u0026rsquo;ve been meaning to write more about my Home Assistant setup, but I\u0026rsquo;m not sure what would be useful to share. For now I\u0026rsquo;ll write what\u0026rsquo;s on my mind, but feel free to get in touch if you want me to share more.\u003c/p\u003e\n\u003cp\u003eI started off my Home Assistant journey by purchasing smart bulbs and smart switches flashed with the \u003ca href=\"https://tasmota.github.io/docs/\"\u003eTasmota firmware\u003c/a\u003e. The devices specifically connected to my local WiFi network and sent messages to my MQTT server.\u003c/p\u003e\n\u003cp\u003eHowever, I wasn\u0026rsquo;t the happiest with the quality of my smart bulbs. I came across itchaboyagin\u0026rsquo;s post on the \u003ca href=\"https://community.home-assistant.io/t/i-just-finished-testing-over-150-of-the-best-smart-lights-here-s-all-the-data/764760\"\u003eHome Assistant forum\u003c/a\u003e where they shared a \u003ca href=\"https://optimizeyourbiology.com/smart-light-database/\"\u003edatabase\u003c/a\u003e of 120 different smart bulbs full of metrics. What I was looking for at the time escaped by head, but I believe I focused on bulbs with no flicker risk and great \u003ca href=\"https://en.wikipedia.org/wiki/Color_rendering_index\"\u003ecolor quality\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThat\u0026rsquo;s when I came across the Innr bulb and noticed that it relied on Zigbee for connectivity.\nThis means that instead of connecting to my WiFI, it creates it\u0026rsquo;s own mesh network and requires a hub to work. So\u0026hellip; I needed to purchase an extra device. Luckily, many companies support the Zigbee standard, so I told myself that this would open up oppurtunities for future devices.\u003c/p\u003e\n\u003cp\u003eOverall, I\u0026rsquo;ve been rocking this new setup for the last 6 months and I\u0026rsquo;m happy.\u003c/p\u003e\n",
    "date_published": "2026.05.22",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/expectations-are-linear/",
    "url": "https://brandonrozek.com/blog/expectations-are-linear/",
    "title": "Expectations are Linear",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cblockquote\u003e\n\u003cp\u003eAs an example, he asked me, in more words, what the expected rank when flipping over the top card of a deck of cards was (A=1, J=11, Q=12,  K=13). This is easy to compute directly as 7. Then he asked me the expectation of the \u003cem\u003esum of the top two cards\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003e- \u003ca href=\"https://buttondown.com/jaffray/archive/expectation-and-copysets/\"\u003eFrom \u0026ldquo;Expectation and Copysets\u0026rdquo; by Justin Jaffray\u003c/a\u003e\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eWhat does your intuition say the answer is? Justin continues by stating that computing this expectation is as easy as summing their individual expectations.\n$$\nE[X + Y] = E[X] + E[Y]\n$$\nIn other words, \u003cstrong\u003eexpectations are linear\u003c/strong\u003e. I recommend reading his entire blog post. It\u0026rsquo;s great and also talks about how this property is used in databases today. After a high-level explanation, he says:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eThe fact that expectation is linear is easy to show if you just look at the definition, which we will not do here, but I trust you are capable of if you are interested and have not already seen it.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eIn this episode of \u003cem\u003eExercise for the Reader\u003c/em\u003e (\u003ca href=\"/blog/implications-prenex-normal-form/\"\u003elast episode\u003c/a\u003e), we\u0026rsquo;ll look at the definition and show why this property holds. This is true regardless of the underlying probability distribution and whether or not we\u0026rsquo;re sampling with replacement.\u003c/p\u003e\n\u003cp\u003eAs Justin stated, let\u0026rsquo;s start with the definition of expectation and then split the sum:\n$$\n\\begin{align*}\nE[X + Y] \u0026amp;= \\sum_{x \\in X} \\sum_{y \\in Y} (x + y) \\cdot P(X = x, Y=y) \\\\\n\u0026amp;= (\\sum_{x \\in X} \\sum_{y \\in Y} x \\cdot P(X = x, Y = y)) + (\\sum_{x \\in X} \\sum_{y \\in Y} y \\cdot P(X = x, Y = y))\n\\end{align*}\n$$\nNotice that the left-hand-side of the multiplication does not depend on both variables anymore. Also it doesn\u0026rsquo;t matter whether we do the summation over $X$ first or $Y$. Therefore, we can bring that variable out of the inner sum and simplify this to:\n$$\nE[X + Y] = (\\sum_{x \\in X} x \\sum_{y \\in Y} P(X = x, Y = y)) + (\\sum_{y \\in Y} y \\sum_{x \\in X} P(X = x, Y = y))\n$$\nWe can then perform \u003cem\u003emarginalization\u003c/em\u003e to substitute $\\sum_{y \\in Y} P(X = x, Y = y)$ with $P(X = x)$ and do the same for the right hand side of the sum.\n$$\n\\begin{align*}\nE[X + Y] \u0026amp;= (\\sum_{x \\in X}xP(X = x)) + (\\sum_{y \\in y}yP(Y=y))) \\\\\n\u0026amp;= E[x] + E[Y]\n\\end{align*}\n$$\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eWhy can we marginalize? For me to show why, we need to peel back the curtain on the notation.\u003c/p\u003e\n\u003cp\u003eThe set $\\Omega$ contains the outcomes of all the events that we\u0026rsquo;re concerned about. So, if we are considering events $X$ and $Y$ with outcomes $x_i$ and $y_i$, respectively. Then, our event space $\\Omega$ is equal to $\\{ (x_i, y_i) \\mid x_i \\in X, y_i \\in Y\\}$.\u003c/p\u003e\n\u003cp\u003eTherefore when we say $X = x$, what we really mean is the set of outcomes where that is true. In mathematical terms, $\\{\\omega \\in \\Omega \\mid X(\\omega) = x\\}$.\u003c/p\u003e\n\u003cp\u003eNow, let\u0026rsquo;s show why $\\sum_{y \\in Y} P(X = x, Y = y) = P(X = x)$.\n$$\n\\sum_{y \\in Y}P(X = x, Y = y) = \\sum_{y \\in Y} P(\\{\\omega \\in \\Omega \\mid X(\\omega) = x\\} \\cap \\{\\omega \\in \\Omega \\mid Y(\\omega) = y\\})\n$$\nOne of the three Kolmogorov axioms of probability is \u003cstrong\u003ecountable additivity\u003c/strong\u003e. This is defined as:\n$$\n\\sum_{x \\in A}P(X = x) = P(\\bigcup_{x \\in A}X =x )\n$$\nSubstituting that in and simplifying, we get:\n$$\n\\begin{align*}\n\\sum_{y \\in Y}P(X = x, Y = y) \u0026amp;= P(\\bigcup_{y \\in Y}(\\{\\omega \\in \\Omega \\mid X(\\omega) = x\\} \\cap \\{\\omega \\in \\Omega \\mid Y(\\omega) = y\\})) \\\\\n\u0026amp;= P(\\{\\omega \\in \\Omega \\mid X(\\omega) = x\\} \\cap \\bigcup_{y \\in Y}\\{\\omega \\in \\Omega \\mid Y(\\omega) = y\\})\n\\end{align*}\n$$\nNotice that the right hand side of the term is just $\\Omega$. We can then simplify to,\n$$\n\\begin{align*}\n\\sum_{y \\in Y}P(X = x, Y = y) \u0026amp;= P(\\{\\omega \\in \\Omega \\mid X(\\omega) = x\\} \\cap \\Omega) \\\\\n\u0026amp;= P(\\{\\omega \\in \\Omega \\mid X(\\omega) = x\\}) \\\\\n\u0026amp;= P(X = x)\n\\end{align*}\n$$\nSince countable additivity is an axiom, we\u0026rsquo;ll stop our derivations there.  See you next time.\u003c/p\u003e\n",
    "date_published": "2026.04.26",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/on-the-tor-network/",
    "url": "https://brandonrozek.com/blog/on-the-tor-network/",
    "title": "Bringing this website to the Tor network",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI believe in the freedom of information. By making my website available as a Tor hidden service, you can be sure to access the information even if it\u0026rsquo;s blocked on the clearweb.\u003c/p\u003e\n\u003cp\u003eIn this post, I\u0026rsquo;ll share the steps I took and what I learned along the way. Huge credit to Christian \u003ca href=\"https://cleberg.net/blog/self-hosting-tor.html\"\u003ewho wrote their own succinct version\u003c/a\u003e of this post and helped me troubleshoot via email.\u003c/p\u003e\n\u003ch3 id=\"getting-an-address\"\u003eGetting an Address\u003c/h3\u003e\n\u003cp\u003eUnlike the clear web, we don\u0026rsquo;t register a domain with anyone. \u003ca href=\"https://github.com/torproject/torspec/blob/main/rend-spec-v3.txt\"\u003eAn address on Tor is a hash of your public key.\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eThis is why onion URLs are long and unreadable. Take a look at the following onion URL which takes you to the Tor homepage.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ehttp://2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNotice that the address starts with \u003ccode\u003ehttp\u003c/code\u003e. Unlike the clearweb, \u003cem\u003eall\u003c/em\u003e traffic via Tor is encrypted. Our hidden service will use the public key behind the URL during the protocol exchange.\u003c/p\u003e\n\u003cp\u003eThe primary benefit of these addresses are that they are entirely decentralized (we create our own keys). They are also incredibly difficult to spoof. The downside is that they are not human readable. We can get a little bit closer to that though with vanity keys.\u003c/p\u003e\n\u003cp\u003eTo create a vanity key, we can use \u003ca href=\"https://github.com/cathugger/mkp224o#requirements\"\u003emkp224o\u003c/a\u003e. After following the installation instructions, we can create our own onion URL starting with some \u003ccode\u003eprefix\u003c/code\u003e by running the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e./mkp224o prefix\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eGiven the current algorithms and hardware, we can easily generate keys which have a URL prefix length of up to six characters in minutes. Beyond that, the generation quickly becomes infeasible\u0026hellip; However, we want this to be the case. If it was feasible to generate a key-pair for an entire onion URL then anyone can spoof your hidden service.\u003c/p\u003e\n\u003cp\u003eThe script by default will create many folders in the current directory which have the onion URLs as their name and the contents of the keys within. Pick your favorite and copy it over to \u003ccode\u003e/var/lib/tor/somehiddenservicename\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWhile you\u0026rsquo;re at it, make a backup of these keys because if we lose it then we lose control over the domain.\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"installing-and-configuring-tor\"\u003eInstalling and Configuring Tor\u003c/h2\u003e\n\u003cp\u003eNow that we have our address. Let\u0026rsquo;s get Tor installed and set up. In this guide, I\u0026rsquo;ll show how to do so via Podman Quadlets.\u003c/p\u003e\n\u003cp\u003eWe\u0026rsquo;ll use the Docker container \u003ca href=\"https://github.com/dockur/tor\"\u003edockur/tor\u003c/a\u003e. Since Tor is a cryptographic software, we need to be extremely careful where we download it from. As of this time of writing if we look at the Dockerfile in this repo, we can see that this is a simple wrapper over Alpine\u0026rsquo;s tor packages. Currently, I feel that it\u0026rsquo;s safe to trust the Alpine maintainers.\u003c/p\u003e\n\u003cp\u003eAfter selecting the Docker container, we need to write the Quadlet definition file. Here\u0026rsquo;s how I have \u003ccode\u003e/etc/containers/systemd/tor.container\u003c/code\u003e configured:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Container]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eContainerName\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etor\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eHostName\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etor\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eImage\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edocker.io/dockurr/tor\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAutoUpdate\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eregistry\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eVolume\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/etc/tor:/etc/tor:ro,Z\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eVolume\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/var/lib/tor:/var/lib/tor:Z,U\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRestart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ealways\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edefault.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBefore we run it, we\u0026rsquo;ll need to create our main Tor configuration file. This lives in \u003ccode\u003e/etc/tor/torrc\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eSocksPort 0\nHiddenServiceDir /var/lib/tor/somehiddenservicename\nHiddenServicePort 80 IP_OF_NGINX_CONTAINER:80\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eReplace \u003ccode\u003eIP_OF_NGINX_CONTAINER\u003c/code\u003e with the internal IP of the Nginx container or of the host machine if it is running on there. Here\u0026rsquo;s an explanation of each line:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eKey\u003c/th\u003e\n          \u003cth\u003eValue\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eSocksPort\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eBy default, the tor service will open a SOCKS port so that we can have other HTTP clients proxy to the dark web. Setting this to 0 disables that behavior.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eHiddenServiceDir\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eThe location of our hostname file and the keys for our hidden service.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eHiddenServicePort\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eThe port on the Tor network followed by the \u003ccode\u003eaddress:port\u003c/code\u003e to proxy the traffic to. We may have multiple of these per hidden service.\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eSince we\u0026rsquo;re proxying traffic to our application, let\u0026rsquo;s configure our target next.\u003c/p\u003e\n\u003ch2 id=\"configuring-nginx\"\u003eConfiguring Nginx\u003c/h2\u003e\n\u003cp\u003eMy website is a static site served by Nginx. These files are linked together via absolute URLs. This adds some complications because the onion hidden service URL is completely different than my clearnet one.\u003c/p\u003e\n\u003cp\u003eOne solution would be to make my URLs relative. However, I want my website to be as portable as possible. For example, you can run my website in a subfolder. Thus, our solution here is to have an entirely separate copy of the website where the only difference is the internal URLs.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve set this version of my website to live in \u003ccode\u003e/var/www/brozekhs\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eAs such, we have to create an Nginx configuration file solely for our hidden service. We\u0026rsquo;ll make sure to point the root to the folder which contains the hidden service version of my website.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eserver\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eserver_name\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eONION_URL\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003eroot\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/var/www/brozekhs\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eindex\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eindex.html\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e# ...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003elocation\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#f92672\"\u003eallow\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eIP_OF_TOR_CONTAINER\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003edeny\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eall\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# ...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReplace \u003ccode\u003e/var/www/brozekhs\u003c/code\u003e, \u003ccode\u003eONION_URL\u003c/code\u003e and \u003ccode\u003eIP_OF_TOR_CONTAINER\u003c/code\u003e with their respective values. We need to configure the \u003ccode\u003eallow\u003c/code\u003e and \u003ccode\u003edeny\u003c/code\u003e lines because we don\u0026rsquo;t want someone to confirm that our machine responds to tor addresses over the clearnet.\u003c/p\u003e\n\u003cp\u003eAfter this when running my \u003ccode\u003enginx\u003c/code\u003e container I came across the following error:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edate time [emerg] 1#1: could not build server_names_hash, you should increase server_names_hash_bucket_size: 64\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAs stated, the solution is to increase the server name hash bucket size. I doubled it from 64 to 128 in the \u003ccode\u003ehttp\u003c/code\u003e block of \u003ccode\u003e/etc/nginx/nginx.conf\u003c/code\u003e:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eserver_names_hash_bucket_size 128;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eLastly if you have a clearnet site and you want to advertise the onion URL for users visiting the clearnet URL on the Tor browser, then we can add an \u003ca href=\"https://community.torproject.org/onion-services/advanced/onion-location/\"\u003e\u003ccode\u003eOnion-Location\u003c/code\u003e header\u003c/a\u003e to the Nginx config of our clearnet site.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003elocation\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e# ...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    \u003cspan style=\"color:#f92672\"\u003eadd_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eOnion-Location\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eONION_URL\u003c/span\u003e$request_uri;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReplace \u003ccode\u003eONION_URL\u003c/code\u003e with your own and make sure that \u003ccode\u003e$request_uri\u003c/code\u003e stays at the end of the header value. This will help the Tor browser with the redirection.\u003c/p\u003e\n\u003ch2 id=\"start-em-up\"\u003eStart em up!\u003c/h2\u003e\n\u003cp\u003eWith that, we can start our two services:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esudo systemctl start nginx\nsudo systemctl start tor\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWhen we look at \u003ccode\u003ejournalctl -U tor\u003c/code\u003e, we should see something like the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edatetime hostname systemd[1]: Started tor.service.\ndatetime hostname tor[681220]: datetime [notice] Tor can\u0026#39;t help you if you use it wrong! Learn how to be safe at https://support.torproject.org/faq/staying-anonymous/\ndatetime hostname tor[681220]: datetime [notice] Read configuration file \u0026#34;/etc/tor/torrc\u0026#34;.\ndatetime hostname tor[681220]: datetime [notice] Parsing GEOIP IPv4 file /usr/share/tor/geoip.\ndatetime hostname tor[681220]: datetime [notice] Parsing GEOIP IPv6 file /usr/share/tor/geoip6.\ndatetime hostname tor[681220]: datetime [notice] Bootstrapped 0% (starting): Starting\ndatetime hostname tor[681220]: datetime [notice] Starting with guard context \u0026#34;default\u0026#34;\ndatetime hostname tor[681220]: datetime [notice] Bootstrapped 5% (conn): Connecting to a relay\ndatetime hostname tor[681220]: datetime [notice] Bootstrapped 10% (conn_done): Connected to a relay\ndatetime hostname tor[681220]: datetime [notice] Bootstrapped 14% (handshake): Handshaking with a relay\ndatetime hostname tor[681220]: datetime [notice] Bootstrapped 15% (handshake_done): Handshake with a relay done\ndatetime hostname tor[681220]: datetime [notice] Bootstrapped 45% (requesting_descriptors): Asking for relay descriptors\ndatetime hostname tor[681220]: datetime [notice] Bootstrapped 50% (loading_descriptors): Loading relay descriptors\ndatetime hostname tor[681220]: datetime [notice] Bootstrapped 55% (loading_descriptors): Loading relay descriptors\ndatetime hostname tor[681220]: datetime [notice] Bootstrapped 60% (loading_descriptors): Loading relay descriptors\ndatetime hostname tor[681220]: datetime [notice] Bootstrapped 66% (loading_descriptors): Loading relay descriptors\ndatetime hostname tor[681220]: datetime [notice] Bootstrapped 75% (enough_dirinfo): Loaded enough directory info to build circuits\ndatetime hostname tor[681220]: datetime [notice] Bootstrapped 90% (ap_handshake_done): Handshake finished with a relay to build circuits\ndatetime hostname tor[681220]: datetime [notice] Bootstrapped 95% (circuit_create): Establishing a Tor circuit\ndatetime hostname tor[681220]: datetime [notice] Bootstrapped 100% (done): Done\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe won\u0026rsquo;t be able to access the hidden service until the bootstrapping process reaches 100%. Depending on the state of the network, this might take awhile\u0026hellip;\u003c/p\u003e\n\u003cp\u003eI mention the network state since it\u0026rsquo;s common for me to see this in the logs:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edatetime hostname tor[295456]: datetime [warn] Detected possible compression bomb with input size = 17413 and output size = 515472 (compression factor = 29.60)\ndatetime hostname tor[295456]: datetime [warn] Possible compression bomb; abandoning stream.\ndatetime hostname tor[295456]: datetime [warn] Detected possible compression bomb with input size = 23289 and output size = 774624 (compression factor = 33.26)\ndatetime hostname tor[295456]: datetime [warn] Possible compression bomb; abandoning stream.\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis compression or \u003ca href=\"https://en.wikipedia.org/wiki/Zip_bomb\"\u003ezip bomb\u003c/a\u003e appears to be an attempt at a distributed \u003ca href=\"https://en.wikipedia.org/wiki/Denial-of-service_attack\"\u003edenial of service attack\u003c/a\u003e. Luckily, the Tor software is smart enough to guard against this.\u003c/p\u003e\n\u003cp\u003eEvery so often my Tor service goes down and I need to restart the daemon for it to come back up. I haven\u0026rsquo;t gotten around to writing monitoring scripts for my hidden service so if anyone has any tips feel free to get in touch.\u003c/p\u003e\n",
    "date_published": "2026.02.01",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/tales-of-christmas-trees/",
    "url": "https://brandonrozek.com/blog/tales-of-christmas-trees/",
    "title": "Tales of Christmas Trees",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn 2020, Clare and I got our first live Christmas tree. We were living in Virginia at the time, and we showed up to a farm that had many trees planted in rows. They gave us a bow saw, and it was up to us to chop one down and bring it home.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/transportingtree2020.jpg\" alt=\"Brandon hauling his tree in a cart.\"\u003e\u003c/p\u003e\n\u003cp\u003eI enjoyed having a live tree in my apartment. I still remember the smell of fresh conifer. After several weeks, the holidays pass and the needles start falling to the ground. I don\u0026rsquo;t quite remember how we got rid of this tree, but I wouldn\u0026rsquo;t be surprised if we just threw it in our dumpster.\u003c/p\u003e\n\u003cp\u003eWe later moved to New York. For the next few years, we would travel to see friends and family during the holidays. I used that as an excuse to not have a tree setup at home. Eventually Clare convinces me that we should still be festive at home, and we bought a plastic tree.\u003c/p\u003e\n\u003cp\u003eAround us in upstate New York, there are several bonfire events in January. There\u0026rsquo;s one in particular that we\u0026rsquo;ve attended for several New Year\u0026rsquo;s Eve. What\u0026rsquo;s special about this bonfire is that the fuel of the fire is not prepared as chopped wood in advance. Instead, it is sourced from the community.\u003c/p\u003e\n\u003cp\u003eLet me paint a picture. It\u0026rsquo;s a cold winter night, and there is a group of around 20-30 people bundled up in full winter gear staying close to the bonfire. As time progresses, the fire gets smaller and smaller, and we get closer to try to feel some of its heat.  Then, a person in a pickup truck arrives, gets out of their vehicle, and chucks their Christmas tree from the bed of the truck onto the fire. The fire rages.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/bonfire2025.jpg\" alt=\"Christmas tree burning in a bonfire\"\u003e\u003c/p\u003e\n\u003cp\u003eNow I find this very cool, but some trees have different fates. Ton writes on \u003ca href=\"https://www.zylstra.org/blog/2026/01/not-the-elves/\"\u003ehis blog\u003c/a\u003e that they have the same tree every year. Instead of sawing their tree down, the tree gets delivered to their place with the root ball intact. Once the holidays are over, the growers come back and pick it up.\u003c/p\u003e\n\u003cp\u003eThrough an email exchange, I learn that this is a \u003ca href=\"https://adopteereenkerstboom.nl/\"\u003enational service\u003c/a\u003e over in the Netherlands. Now that\u0026rsquo;s pretty cool.\u003c/p\u003e\n",
    "date_published": "2026.01.25",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/blogging-as-dialogue-invitation/",
    "url": "https://brandonrozek.com/blog/blogging-as-dialogue-invitation/",
    "title": "Blogging as an Invitation for Dialogue",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWe have many ways to share ideas today. We can:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eText\u003c/li\u003e\n\u003cli\u003eEmail\u003c/li\u003e\n\u003cli\u003ePen a letter\u003c/li\u003e\n\u003cli\u003ePost on a microblog (Mastodon/X/Pixelfed/etc.)\u003c/li\u003e\n\u003cli\u003eWrite a blog post\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eBut not all of these methods inherently create a conversation or dialogue. When I write a technical blog post, I don\u0026rsquo;t expect a reply. Similarly, when I toot on Mastodon, I\u0026rsquo;m fine if no one favorited the post. As such, (micro-)blogging differs greatly from texting and calling someone and is instead much closer to recording a postcast or uploading a video \u0026ndash; a one-way transmission of information.\u003c/p\u003e\n\u003cp\u003ePloum wrote about how he views the \u003ca href=\"https://ploum.net/2025-12-15-communication-entertainment.html\"\u003eActivityPub protocol as a conversation\u003c/a\u003e, and as such servers should not filter posts based on \u003ca href=\"https://ploum.net/2025-12-04-pixelfed-against-fediverse.html\"\u003ewhether they have a picture\u003c/a\u003e. Now I\u0026rsquo;m not on Pixelfed, so I do not have a stake in this issue. However since I view Pixelfed as a microblogging platform, I tend to see it as more of a one-way transmission of information rather than soliciting a response from my friends. This view would put me in the category of folks that view AcitvityPub as a \u0026ldquo;content consumption platform.\u0026rdquo; Though I find that term derogative\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e.\u003c/p\u003e\n\u003cp\u003eNow should Pixelfed filter posts based on whether it contains a picture? I\u0026rsquo;m not sure. But if I was on Mastodon and I specifically @\u0026rsquo;d  someone on Pixelfed,  then I would sure hope that either they received that message or I was shown an error.\u003c/p\u003e\n\u003cp\u003eSo if we\u0026rsquo;re not having a conversation with these (micro-)blog posts, what are we doing? Some of us are trying to teach, keep a public journal, or share our perspectives.  Some of us don\u0026rsquo;t even want responses\u003csup id=\"fnref:2\"\u003e\u003ca href=\"#fn:2\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e2\u003c/a\u003e\u003c/sup\u003e. Though I find that many of us do:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eBlog posts become invitations that never expire\n- \u003ca href=\"https://dri.es/20-years-of-blogging\"\u003eDries\u003c/a\u003e\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eWhen I send a postcard to a loved one, they don\u0026rsquo;t need to reply. However, I send it because it\u0026rsquo;s an acknowledgement of our relationship and it\u0026rsquo;s an invitation to reach out.\u003c/p\u003e\n\u003cp\u003eSimilarly when I write a blog post, by default I\u0026rsquo;m only transmitting information. However, any of you readers can choose to promote this from a transmission to an exchange. From a communication to a dialogue.\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eThe idea that folks are doom-scrolling and are only consuming empty calories misses how communities are  formed on these platforms \u0026ndash; complete with their own social norms.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:2\"\u003e\n\u003cp\u003eEvery so often I get sad when I want to reply to someone, but I don\u0026rsquo;t see an email to reach out to.\u0026#160;\u003ca href=\"#fnref:2\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2026.01.15",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/backups-with-restic-btrfs/",
    "url": "https://brandonrozek.com/blog/backups-with-restic-btrfs/",
    "title": "Backing up my data with Restic, Btrfs, and MinIO",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eFor the past year, I settled on a backup strategy that serves my needs. In this post, I\u0026rsquo;ll share the properties I look for in a backup solution and how my current solution addresses them. As always, if you have any suggestions or improvements, feel free to get in touch.\u003c/p\u003e\n\u003cp\u003eThe first step before talking technology is to identify exactly what data we want to backup. In my homelab, I rely on \u003ca href=\"https://immich.app/\"\u003eImmich\u003c/a\u003e for photo storage, \u003ca href=\"https://www.navidrome.org/\"\u003eNavidrome\u003c/a\u003e for music streaming, databases for my website, and other personal documents. This amounts to a little over 250 GB of data that would be very difficult for me to replace if it was lost.\u003c/p\u003e\n\u003cp\u003eNot all my data lives on that one server though. I also have a storage VPS which runs \u003ca href=\"https://nextcloud.com/\"\u003eNextcloud\u003c/a\u003e and \u003ca href=\"https://hedgedoc.org/\"\u003eHedgedoc\u003c/a\u003e. Both of those services combined have less than 100 GB of data, but just like the homelab, that data is precious. The storage VPS has a total capacity of 1.5 TB.\u003c/p\u003e\n\u003cp\u003eThe \u003ca href=\"https://www.backblaze.com/blog/the-3-2-1-backup-strategy/\"\u003e3-2-1 backup strategy\u003c/a\u003e coined by Peter Krogh suggests that we should have three copies of our data, stored on two different media, and with one being off-site.\u003c/p\u003e\n\u003cp\u003eBoth of my servers have enough storage capacity to hold a copy of all of my data. For my third copy, I decided to rely on \u003ca href=\"https://www.backblaze.com/cloud-storage\"\u003eBackblaze b2\u003c/a\u003e. This is a S3 object storage service that charges low rates for what I store and the number of transactions I make.\u003c/p\u003e\n\u003cp\u003eMy homelab sits in my house, the storage VPS sits in Montreal, and Backblaze stores my data in Phoenix. Therefore, I have \u003cstrong\u003e3\u003c/strong\u003e copies of my data and it\u0026rsquo;s stored in more than \u003cstrong\u003e1\u003c/strong\u003e location.\u003c/p\u003e\n\u003cp\u003eIn order to address having at least \u003cstrong\u003e2\u003c/strong\u003e different storage media, I backup my data using two different approaches: Restic and Btrfs Snapshots. I don\u0026rsquo;t currently have enough data such that hot online storage is cost-prohibitive.\u003c/p\u003e\n\u003ch3 id=\"restic\"\u003eRestic\u003c/h3\u003e\n\u003cp\u003e\u003ca href=\"https://restic.net/\"\u003eRestic\u003c/a\u003e is a modern and open-source backup software that both deduplicates and encrypts file contents at the blob level. In combination with a metadata local cache, this makes this Restic snappy and efficient to use.\u003c/p\u003e\n\u003cp\u003eIt supports a variety of backup targets:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eLocal filesystem\u003c/li\u003e\n\u003cli\u003eSFTP\u003c/li\u003e\n\u003cli\u003eREST server\u003c/li\u003e\n\u003cli\u003eS3\u003c/li\u003e\n\u003cli\u003eand \u003ca href=\"https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#\"\u003eseveral others\u003c/a\u003e, including those implemented by \u003ca href=\"https://rclone.org/\"\u003eRclone\u003c/a\u003e!\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWhile I could use the variety of backup targets to increase the number of approaches my data is backed up, to me, this does not feel sufficiently different enough to warrent the additional complexity. Therefore, I\u0026rsquo;ll use one backup target for all three locations.\u003c/p\u003e\n\u003cp\u003eSince Backblaze b2 only supports the S3 protocol, this means then that my choice has been made for me. However even if it wasn\u0026rsquo;t forced upon me, I would still pick that option. Designed by Amazon for their storage service, the S3 protocol is built with scale in mind and supports concurrent multipart file uploads. Additionally unlike SFTP, S3 separates its accounts from that of the server. There are ways to restrict SSH clients, however, it is not the default behavior. In S3, by default a user cannot do anything.\u003c/p\u003e\n\u003cp\u003eThus, after choosing S3, I need to set up a S3 server on both my storage VPS and my homelab. I landed on MinIO for its ease of setup and longevity, however, any of the other solutions would work as well.\u003c/p\u003e\n\u003cp\u003eSince, we\u0026rsquo;re storing copies of the data on all the servers, one idea is to set it up in a cluster configuration. However, since I\u0026rsquo;m not managing the S3 server hosted by Backblaze, I wouldn\u0026rsquo;t be able to include that in the cluster. Generally, it is not recommended to run a cluster with only two nodes. If we do, it can lead to what\u0026rsquo;s called a \u003ca href=\"https://en.wikipedia.org/wiki/Split-brain_(computing)\"\u003e\u003cem\u003esplit brain problem\u003c/em\u003e\u003c/a\u003e where both nodes are unable to communicate with each other and think that they are the leader node.\u003c/p\u003e\n\u003cp\u003eTo avoid this, we can run both MinIO instances inpendently and have Restic separately send the data to all servers.\u003c/p\u003e\n\u003ch4 id=\"minio-setup\"\u003eMinIO Setup\u003c/h4\u003e\n\u003cp\u003eIf you already have a bucket setup and configured with the appropriate permissions, then feel free to skip this section.\u003c/p\u003e\n\u003cp\u003eThe following code examples assume that \u003ca href=\"https://min.io/docs/minio/container/index.html\"\u003eMinIO is installed\u003c/a\u003e and the \u003ccode\u003emc\u003c/code\u003e client is available with the root credentials to each server (alias) set. These general steps can also be achieved using the management UI. For simplicity, I\u0026rsquo;ll show how to do this with respect to one server \u003ccode\u003ehomelab\u003c/code\u003e. However, we\u0026rsquo;ll need to do this for every MinIO instance.\u003c/p\u003e\n\u003cp\u003eFirst, \u003ca href=\"https://min.io/docs/minio/linux/reference/minio-mc/mc-mb.html\"\u003ecreate a bucket\u003c/a\u003e which will hold our backup data.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# alias_of_server/bucket_name\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emc mb homelab/backups\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAfterwards, create a \u003ca href=\"https://min.io/docs/minio/linux/administration/identity-access-management/minio-user-management.html\"\u003ebackup user\u003c/a\u003e. We\u0026rsquo;ll need to specify the \u003ccode\u003eACCESS_KEY\u003c/code\u003e and \u003ccode\u003eSECRET_KEY\u003c/code\u003e which corresponds to the username and password respectively. Store these as we\u0026rsquo;ll later need to use them.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport ACCESS_KEY\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;my_awesome_homelab_user\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport SECRET_KEY\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;SUPER_SECURE_SECRET_KEY\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emc admin user add homelab $ACCESSKEY $SECRETKEY\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cem\u003eNote:\u003c/em\u003e For a bit more security, I like to create a different access key and secret key for the other servers.\u003c/p\u003e\n\u003cp\u003eBy default, our user cannot cannot do anything. Let\u0026rsquo;s make it so that they have access to our \u003ccode\u003ebackups\u003c/code\u003e bucket we created earlier. To do that, we\u0026rsquo;ll need to create a \u003ca href=\"https://min.io/docs/minio/linux/administration/identity-access-management/policy-based-access-control.html\"\u003epolicy\u003c/a\u003e which allows the user to perform operations on that bucket.\u003c/p\u003e\n\u003cp\u003eWe\u0026rsquo;ll have to create a JSON file and follow the AWS IAM format.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-iam\" data-lang=\"iam\"\u003e{\n \u0026#34;Version\u0026#34;: \u0026#34;2012-10-17\u0026#34;,\n \u0026#34;Statement\u0026#34;: [\n  {\n   \u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;,\n   \u0026#34;Action\u0026#34;: [\n    \u0026#34;s3:ListBucket\u0026#34;,\n    \u0026#34;s3:PutObject\u0026#34;,\n    \u0026#34;s3:DeleteObject\u0026#34;,\n    \u0026#34;s3:GetObject\u0026#34;\n   ],\n   \u0026#34;Resource\u0026#34;: [\n    \u0026#34;arn:aws:s3:::backups/*\u0026#34;,\n    \u0026#34;arn:aws:s3:::backups\u0026#34;\n   ]\n  }\n ]\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAssume that we saved the prior JSON at a location specified by the environmental variable \u003ccode\u003e$POLICY_PATH\u003c/code\u003e, then we can create the policy through the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport POLICY_NAME\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;backup-policy\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emc admin policy create homelab $POLICY_NAME $POLICY_PATH\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAfter creating the policy, we need to assign it to our backup user.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emc admin policy attach homelab $POLICY_NAME --user $ACCESS_KEY\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"nginx-and-initializing-restic\"\u003eNginx and Initializing Restic\u003c/h4\u003e\n\u003cp\u003eWith our bucket and user configured, now let\u0026rsquo;s discuss how we\u0026rsquo;ll perform our backups. Our bucket will have the following directory structure:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ebackups/\n  homelab/\n  vps/\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eEach folder will contain a restic repository. We separate them out instead of having one giant restic repository, so that we don\u0026rsquo;t have to worry about multiple servers competing for a lock.\u003c/p\u003e\n\u003cp\u003eInstead of communicating over HTTP, we want a secure way to access our buckets from outside the server. For this, I use \u003ca href=\"https://min.io/docs/minio/linux/integrations/setup-nginx-proxy-with-minio.html\"\u003enginx to proxy the traffic over to MinIO\u003c/a\u003e. I configure \u003ca href=\"https://letsencrypt.org/getting-started/\"\u003eCertbot with LetsEncrypt\u003c/a\u003e so that the connection can be secureted with HTTPS/TLS. In addition to the instructions listed on the MinIO page, I also restrict the IPs which are allowed to connect. For example, to only allow traffic from within your Wireguard network (ex subnet: \u003ccode\u003e10.10.10.1/24\u003c/code\u003e) then you can put the following within the \u003ccode\u003elocation\u003c/code\u003e block of the nginx config.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eallow\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e.10.10.1/24\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edeny\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eall\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003e//\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eDenies\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eall\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eother\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003etraffic\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAfter securing the entryway to our S3 server, we\u0026rsquo;re ready to use Restic. Before jumping in, you might want to \u003ca href=\"https://restic.readthedocs.io/en/stable/080_examples.html#full-backup-without-root\"\u003ecreate a dedicated account\u003c/a\u003e so that we don\u0026rsquo;t have to use the \u003ccode\u003eroot\u003c/code\u003e  user.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s initialize each server\u0026rsquo;s respective repository. Since our backups will be encrypted, we need to come up with another password and put it within the \u003ccode\u003eRESTIC_PASSWORD\u003c/code\u003e environmental variable. Save this password in a safe place, since we will not be able to decrypt the backup without it.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eexport RESTIC_REPOSITORY=s3:https://\u0026lt;domain-of-s3-server\u0026gt;/backups/homelab\nexport AWS_ACCESS_KEY_ID=$ACCESS_KEY\nexport AWS_SECRET_ACCESS_KEY=$SECRET_ACCESS_KEY\nexport RESTIC_PASSWORD=\u0026#34;RESTIC_SUPER_SECURE_PASSWORD\u0026#34;\n\nrestic init\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFrom here, create a backup script at \u003ccode\u003e/usr/local/bin/backup.sh\u003c/code\u003e. This is where we specify which folders to backup.\u003c/p\u003e\n\u003cp\u003eExample:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/sh\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o errexit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o nounset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$EUID\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -ne \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eid -u restic\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Please run as restic\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  exit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Usage: rbackup [message] [restic-tag] [backup-dir]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erbackup \u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  restic backup \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e    --tag \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$2\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$3\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## BACKUP TO CLOUD\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport RESTIC_REPOSITORY\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003es3:https://\u0026lt;domain-of-cloud-server\u0026gt;/backups/homelab\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport AWS_ACCESS_KEY_ID\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;CLOUD_ACCESS_KEY\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport AWS_SECRET_ACCESS_KEY\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;CLOUD_SECRET_ACCESS_KEY\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport RESTIC_PASSWORD\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;RESTIC_SUPER_SECURE_PASSWORD\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erbackup \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Backing up documents\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  Documents \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  /home/user/Documents\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Backup other great directories...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## BACKUP TO BACKBLAZE\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport RESTIC_REPOSITORY\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003es3:https://\u0026lt;domain-of-backblaze-server\u0026gt;/backups/homelab\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport AWS_ACCESS_KEY_ID\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;BACKBLAZE_ACCESS_KEY\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport AWS_SECRET_ACCESS_KEY\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;BACKBLAZE_SECRET_ACCESS_KEY\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport RESTIC_PASSWORD\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;RESTIC_SUPER_SECURE_PASSWORD\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erbackup \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Backing up documents\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  Documents \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  /home/user/Documents\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Backup other great directories...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUnless you have unlimited storage, you probably want to prune old backups according to some schedule. In the same script I have the following function:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprune \u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Pruning old snapshots\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  restic unlock\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  restic forget \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e    --group-by \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;tags\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e    --keep-daily N_DAYS \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e    --keep-weekly N_WEEKS \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e    --keep-monthly N_MONTHS \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e    --prune\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReplace \u003ccode\u003eN_*\u003c/code\u003e to your liking. Restic will then ensure that it keeps enough snapshots such that those time-based rules are satisfied.\u003c/p\u003e\n\u003cp\u003eWith the script written, we can then setup a systemd service and timer so that it runs daily. Write the following to \u003ccode\u003e/etc/systemd/system/restic-backup.service\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eExecutes backup script\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRequires\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enetwork-online.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWants\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eUser\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003erestic\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eGroup\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003erestic\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eoneshot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/local/bin/backup.sh\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eEnvironment\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;HOME=/home/restic\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWrite the timer to \u003ccode\u003e/etc/systemd/system/restic-backup.timer\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Timer]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eOnCalendar\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edaily\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePersistent\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etimers.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"btrfs-snapshots\"\u003eBtrfs snapshots\u003c/h3\u003e\n\u003cp\u003eBoth my servers run Btrfs as the underlying filesystem. One cool feature is that Btrfs can create \u003cem\u003esnapshots\u003c/em\u003e. These are immutable point-in-time views of a given subvolume. As such, we\u0026rsquo;ll need to create a subvolume that will contain the directories we want.\u003c/p\u003e\n\u003cp\u003eIf we\u0026rsquo;re not starting from scratch, and instead want to create a subvolume from an existing folder, then I recommend performing the following steps from \u003ca href=\"https://www.reddit.com/r/btrfs/comments/198hbod/converting_directory_into_subvolume/\"\u003ethis reddit thread\u003c/a\u003e:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003emv folder folder_backup\nbtrfs subvolume create folder\ncp --archive --one-file-system --reflink=always folder_backup/. folder\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFrom here, we can create our snapshots. Like with our Restic setup, I like having some daily, weekly, and monthly backups available. I\u0026rsquo;ll store these snapshots in \u003ccode\u003e/snapshots\u003c/code\u003e, but you\u0026rsquo;re free to change the location. Here\u0026rsquo;s what it looks like on my machine:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/snapshots/\n  daily\n    20251201\n      /home/user/Documents\n      ...\n      /home/user/Music\n    ...\n    20251227\n  monthly\n    ...\n  weekly\n    ...\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eSince these are backup snapshots, we want it to be \u003cem\u003ereadonly\u003c/em\u003e. Here is what it looks like to create a daily snapshot for our documents subvolume:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSNAPSHOT_PATH\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/snapshots/daily/\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003edate +\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;%Y%m%d\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/home/user/Documents\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir -p \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003edirname $SNAPSHOT_PATH\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebtrfs subvolume snapshot -r /home/user/Documents \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSNAPSHOT_PATH\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe used the date in the folder name so that we can easily detect the oldest snapshots for deletion.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eOLDEST_SNAPSHOT\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003els \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSNAPSHOT_DIR\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | sort | head -n 1\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e SUBVOLUME_PATH in \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSUBVOLUMES[@]\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    SNAPSHOT_PATH\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSNAPSHOT_DIR\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eOLDEST_SNAPSHOT\u003cspan style=\"color:#e6db74\"\u003e}${\u003c/span\u003eSUBVOLUME_PATH\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    btrfs subvolume delete -c \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSNAPSHOT_PATH\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erm -rf \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSNAPSHOT_DIR\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eOLDEST_SNAPSHOT\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI put together a script which takes as input the environment variable \u003ccode\u003eSNAPSHOT_DIR\u003c/code\u003e and handles creating and pruning snapshots. To get these snapshots at different time intervals, we use different systemd timers and change that input variable. Unlike with our restic setup, this will keep the same $N$ number of snapshots for each of our time intervals.  We copy this script to \u003ccode\u003e/usr/local/bin/localbtrbak.sh\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o nounset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eshow_usage\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Usage: localbtrback\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check argument count\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$#\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -ne \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    show_usage\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$EUID\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -ne \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Please run as root\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e -z \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$SNAPSHOT_DIR\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;SNAPSHOT_DIR not defined\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# EDIT TO POINT TO YOUR SUBVOLUMES\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSUBVOLUMES\u003cspan style=\"color:#f92672\"\u003e=(\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/home/user/Documents\u0026#34;\u003c/span\u003e  \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/home/user/Music\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e SUBVOLUME_PATH in \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSUBVOLUMES[@]\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    SNAPSHOT_PATH\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSNAPSHOT_DIR\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003edate +\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;%Y%m%d\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSUBVOLUME_PATH\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Create folder if not already exists\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    mkdir -p \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003edirname $SNAPSHOT_PATH\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Create the readonly snapshot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    btrfs subvolume snapshot -r \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSUBVOLUME_PATH\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSNAPSHOT_PATH\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Calculate the number of snapshots\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCOUNT\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003els \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSNAPSHOT_DIR\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | wc -l\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eCOUNT\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -gt \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    OLDEST_SNAPSHOT\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003els \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSNAPSHOT_DIR\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | sort | head -n 1\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e SUBVOLUME_PATH in \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSUBVOLUMES[@]\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        SNAPSHOT_PATH\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSNAPSHOT_DIR\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eOLDEST_SNAPSHOT\u003cspan style=\"color:#e6db74\"\u003e}${\u003c/span\u003eSUBVOLUME_PATH\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        btrfs subvolume delete -c \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSNAPSHOT_PATH\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    rm -rf \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSNAPSHOT_DIR\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eOLDEST_SNAPSHOT\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor our systemd service in \u003ccode\u003e/etc/systemd/system/btrlocalback@.service\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eCreate a local BTRFS snapshot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eoneshot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eEnvironment\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eSNAPSHOT_DIR=\u0026#34;/snapshots/%i\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/local/bin/localbtrbak.sh\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAn example daily timer stored in \u003ccode\u003e/etc/systemd/system/btrlocalbak@daily.timer\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eCreate a daily local BTRFS snapshot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Timer]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eOnCalendar\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edaily\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePersistent\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etimers.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"conclusion\"\u003eConclusion\u003c/h3\u003e\n\u003cp\u003eHere is a visualization of the three servers and where the data gets backed up to:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/BackupSetup2025.svg\" alt=\"Diagram of the backup setup I described\"\u003e\u003c/p\u003e\n\u003cp\u003eThe bucket image in the diagram denotes the S3 storage, while the cylinder database icon denotes the Btrfs snapshots. Pictorially, this also shows us how the 3-2-1 rule is satisfied.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThree outgoing arrows on the two servers means that we have three copies of the data\u003c/li\u003e\n\u003cli\u003eThe two icons on each of the servers show that we\u0026rsquo;re backing it up in two different ways\u003c/li\u003e\n\u003cli\u003eI have more than one off-site backup since all three boxes are in different locations.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIf I accidentally delete a file, the Btrfs setup is useful since I can quickly access the old version at \u003ccode\u003e/snapshots/daily/\u0026lt;yesterday\u0026gt;/path\u003c/code\u003e. However, if my entire server goes down, then I can use one of the restic backups in either server to restore.\u003c/p\u003e\n",
    "date_published": "2025.12.30",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/running-75-miles/",
    "url": "https://brandonrozek.com/blog/running-75-miles/",
    "title": "After 75 Miles of Running",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eBack in August, I started running regularly and recording my sessions. I recently completed a total of 75 miles of running across all my sessions. In this post, I\u0026rsquo;ll reflect on my journey so far and what I\u0026rsquo;ve learned.\u003c/p\u003e\n\u003ch2 id=\"the-beginning\"\u003eThe Beginning\u003c/h2\u003e\n\u003cp\u003eOver the summer, I lived and worked in Austin, Texas. For the prior months of the year, I\u0026rsquo;ve lived a fairly sedimentary lifestyle. I enjoy these summers where I am forced to walk more places and \u003ca href=\"/blog/exploring-via-public-transit/\"\u003etake public transit\u003c/a\u003e since I don\u0026rsquo;t have access to a vehicle.\u003c/p\u003e\n\u003cp\u003eTwo of my coworkers were really into running and one of them even regularly ran a few miles outside in the \u003ca href=\"/blog/embrace-the-heat/\"\u003e95+ degree Fahrenheit weather\u003c/a\u003e. This didn\u0026rsquo;t immediately convince me to start myself, but it definitely planted the seed. Towards the end of the summer, I asked myself \u0026ldquo;What\u0026rsquo;s stopping me from running?\u0026rdquo; and took this as an opportunity to prove something to myself.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eWhat do I need to get started?\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eAt the beginning, I didn\u0026rsquo;t end up buying anything new. In Austin, I wore some sneakers from \u003ca href=\"https://www.keenfootwear.com\"\u003eKeen footwear\u003c/a\u003e which I accidentally soaked a few times in sudden downpours. I also wear a Fitbit watch that has heart rate monitoring built in.\u003c/p\u003e\n\u003cp\u003eWhen I returned to New York, I ended up buying a \u003ca href=\"https://www.brooksrunning.com/en_us\"\u003eBrooks running shoe\u003c/a\u003e. Shoes designed for running are usually lighter and have shock absorption. This makes for a more pleasant workout.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eDid you use a training plan?\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eInitially, I used the \u003ca href=\"https://www.nhs.uk/better-health/get-active/get-running-with-couch-to-5k/couch-to-5k-running-plan/\"\u003ecouch to 5k running plan\u003c/a\u003e developed by the UK\u0026rsquo;s National Health Service (NHS). After completing that plan, I charted out my own path, with most of my sessions consisting of 30 minutes of running surrounded by 5 minutes of walking.\u003c/p\u003e\n\u003cp\u003eWhen I returned to New York, I saw that my city was hosting a \u003ca href=\"https://troyturkeytrot.com/\"\u003eturkey trot\u003c/a\u003e on Thanksgiving. That was a perfect amount of time to follow the program and see how things turn out!\u003c/p\u003e\n\u003ch2 id=\"training-arc\"\u003eTraining Arc\u003c/h2\u003e\n\u003cp\u003eWith a race in mind, I had to make sure to not skimp on my training. Initially, I had an every other day schedule. Though as I adjusted back to my new schedule, this has become a little chaotic.\u003c/p\u003e\n\u003ch3 id=\"running-inside-vs-outside\"\u003eRunning Inside vs Outside\u003c/h3\u003e\n\u003cp\u003eThe transition between summer and fall in New York state is lovely. It makes for a great time to run outside.  However, as the months rolled by it started getting colder and colder. Now that we\u0026rsquo;re in the midst of winter, I\u0026rsquo;ve mostly abandoned running outside.\u003c/p\u003e\n\u003cp\u003eSome people swear by running outdoors versus using a treadmill. I\u0026rsquo;m fine with either, but there are definitely trade-offs.\u003c/p\u003e\n\u003cp\u003eWith outdoor running there is much more visual stimuli. Instead of staring at a wall, the scenery changes as you move around. This is a huge pro when it comes to staving off boredom; which I find very important for a successful workout. The uneven ground also prevents repetitive strain injuries.\u003c/p\u003e\n\u003cp\u003eOn the other hand, it\u0026rsquo;s very easy to keep pace with a treadmill. Another benefit is that you don\u0026rsquo;t have to plan out a route ahead of time. Though, I find it imperative to have some sort of distraction ready for when I do my treadmill runs. Initially I turned to watching YouTube videos, but I noticed that my posture would then suffer. I find podcasts to be a nice compromise.\u003c/p\u003e\n\u003ch3 id=\"run-slowly\"\u003eRun Slowly\u003c/h3\u003e\n\u003cp\u003eI\u0026rsquo;m far from an expert in this area, but it\u0026rsquo;s generally recommended to keep your \u003ca href=\"https://en.wikipedia.org/wiki/Long_slow_distance\"\u003eheart rate low\u003c/a\u003e when you\u0026rsquo;re running. This helps improve your cardiovascular fitness over the long run. An informal test is to see if you can carry a conversation while running.\u003c/p\u003e\n\u003cp\u003eFormally, it\u0026rsquo;s recommended to stay in \u0026ldquo;Zone 2\u0026rdquo; while running which is about 60-70% of your max heart rate.  We can use the Bruce protocol to find out our max heart rate, or for a much simpler approximation, we can use the Tanaka, Monahan, \u0026amp; Seals (2001) formula.\n$$\nhr_{max} = 208 - (0.7 * age)\n$$\nThat means for a 30 year old their zone 2 heart rate lies between 112 and 130.\u003c/p\u003e\n\u003cp\u003eIn Zone 2, the body can still supply energy using fat reserves and clear up the lactate before it builds up. It\u0026rsquo;s easy when running to go past this heart rate. The best way I\u0026rsquo;ve found to keeping it in zone 2 is to run \u003cem\u003ereally slow\u003c/em\u003e. Often much slower than you think you should be running. When I was first starting out, I wasn\u0026rsquo;t going much faster than walking pace.\u003c/p\u003e\n\u003ch3 id=\"running-with-others\"\u003eRunning with others\u003c/h3\u003e\n\u003cp\u003eA friend recently got me to attend the local run club in the city. At first, I was nervous about joining since I thought it would only be full of people who ran their whole life. It turns out that it had people at all different points in the running journey. I joined once a week until it started regularly staying below freezing. I hope to attend again next year.\u003c/p\u003e\n\u003cp\u003eEither way, running can be a great excuse to get together with others!\u003c/p\u003e\n\u003ch2 id=\"the-turkey-trot\"\u003eThe Turkey Trot\u003c/h2\u003e\n\u003cp\u003eWe then arrive at the big day! I showed up around thirty minutes before the event started. To give an idea of the scale, the Troy turkey trot had over 4000 finishers for the 5k.\u003c/p\u003e\n\u003cp\u003eAt the beginning of the race, everyone lined up by flags marking different paces. I timed myself at a nearby track, and my last 5k was 35 minutes. Feeling optimistic, I lined up at the 11 minute flag.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/ttslpaceflags.jpg\" alt=\"High-up view of my starting position\"\u003e\u003c/p\u003e\n\u003cp\u003eBam! The sound of the gun then went off signaling the start of the race \u0026ndash; except there was no action. Since I was so far back from the start line, it took over 5 minutes before me and the others around me got to shuffling.\u003c/p\u003e\n\u003cp\u003eLuckily, when it comes to timing there\u0026rsquo;s the gun time and the net time. The net time is the time it takes once you cross the \u0026ldquo;start line\u0026rdquo; to hit the \u0026ldquo;finish line\u0026rdquo;.\u003c/p\u003e\n\u003cp\u003eWith the majority of my running sessions featuring me, myself, and I, running with so many other people gave a huge energy boost. I let the energy get to me, and I didn\u0026rsquo;t end up running a consistent pace at all. I likely ran a little too fast in the beginning, resulting in short walking breaks towards the end. Also I underestimated the energy it would take to weave around other people.\u003c/p\u003e\n\u003cp\u003eI hit the finish line at \u003ca href=\"https://www.zippy-reg.com/results/live/athlete/index.php?eid=228\u0026amp;bib=1508\"\u003e34 minutes and 14 seconds\u003c/a\u003e! It\u0026rsquo;s pretty exciting to hit a new personal best during a race.\u003c/p\u003e\n\u003cp\u003eFocusing on the race, I didn\u0026rsquo;t end up taking too many photos. Instead here\u0026rsquo;s a blurry snapshot from the \u003ca href=\"https://www.youtube.com/watch?v=fLRtp4DtcB8\"\u003efinish cam\u003c/a\u003e and a selfie of me with my participation medal.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/ttfc2025.png\" alt=\"Screenshot of me passing the finish line with many others\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/ttselfie2025.jpg\" alt=\"Selfie of me with the participation medal\"\u003e\u003c/p\u003e\n\u003ch2 id=\"onto-the-future\"\u003eOnto the future!\u003c/h2\u003e\n\u003cp\u003eWith that milestone completed, now it\u0026rsquo;s time to think about what\u0026rsquo;s next. Do I train for a 10k? A half-marathon? Nothing is set in stone yet, and I\u0026rsquo;m keeping my eye out for future events.\u003c/p\u003e\n\u003cp\u003eI mentioned that one of my challenges with running is starving off boredom. While the race does inject a new form of energy, I still currently can\u0026rsquo;t see running for multiple hours!\u003c/p\u003e\n\u003cp\u003eBeyond running itself, I find it fun to look at my logs. For example, we can take the times from the Troy turkey trot, and plot my performance on the histogram of all participant net times.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/tthistnettime2025.png\" alt=\"Histogram of net times for all the participants\"\u003e\u003c/p\u003e\n\u003cp\u003eAdditionally, from my overall training log, I can see that I run on average 2.5 miles during my 30 minute runs.  I\u0026rsquo;m excited to see how these metrics improve over time.\u003c/p\u003e\n",
    "date_published": "2025.12.26",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/disabling-nat-source-port-randomization-opnsense/",
    "url": "https://brandonrozek.com/blog/disabling-nat-source-port-randomization-opnsense/",
    "title": "Disabling Nat Source Port Randomization on OPNsense for Gaming",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cblockquote\u003e\n\u003cp\u003eLet\u0026rsquo;s play Mario Party tonight!\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eAfter many years of friendship, I\u0026rsquo;ve learned that playing online multiplayer games is almost never as simple as it seems. This night was no different.  In this post, I\u0026rsquo;ll go over what I learned setting up my Nintendo Switch for online play. Luckily, this same concept applies to the PlayStation 5 as well\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e.\u003c/p\u003e\n\u003cp\u003eBut first, I\u0026rsquo;ll take a detour into how peer-to-peer (P2P) gaming typically works. So, feel free to \u003ca href=\"#the-solution\"\u003eskip\u003c/a\u003e down to the solution.\u003c/p\u003e\n\u003ch2 id=\"peer-to-peer-gaming\"\u003ePeer-to-Peer Gaming\u003c/h2\u003e\n\u003cp\u003eMario Party is a board game where you move characters around in hopes of collecting the most stars. You play as a character from the Mario franchise, and between each turn on the board is a minigame. These minigames provide the illusion that this is a skill-based game. But trust me, you can win by just tapping A.\u003c/p\u003e\n\u003cp\u003eSony and Nintendo both aren\u0026rsquo;t forthcoming with information about how their games and systems work. Therefore, I\u0026rsquo;ll be making a number of assumptions here.\u003c/p\u003e\n\u003cp\u003eNintendo owns Mario Party, and from there we can presume that they use the standard Nintendo libraries when building the game. The homebrew community over the years reverse-engineered many of these libraries and due to this we can look at the protocols they use. The Nintendo networking library, called \u003ca href=\"https://github.com/kinnay/NintendoClients/wiki/Pia-Overview\"\u003ePia\u003c/a\u003e, uses a service called NEX to match players in games. This match-making protocol includes a \u003ca href=\"https://en.wikipedia.org/wiki/UDP_hole_punching\"\u003enetwork-address-translation (nat) traversal protocol\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eFor sake of simplicity, I won\u0026rsquo;t describe the protocol in full-depth. What\u0026rsquo;s important to know is that when both players message the server, the server knows each player\u0026rsquo;s IP address and the source port that they used to communicate. The server will then share the other console\u0026rsquo;s information to facilitate that direct peer-to-peer connection\u003c/p\u003e\n\u003cp\u003eUnfortunately, I was not able to find either official or unofficial documentation on how P2P gaming works on the PlayStation 5. From browsing around, I have the impression that this is less standardized and developers are either relying on external SDKs or developing their own libraries.\u003c/p\u003e\n\u003ch2 id=\"the-problem\"\u003eThe Problem\u003c/h2\u003e\n\u003cp\u003eOpnsense, in all their great wisdom, wants to protect me from \u003ca href=\"https://en.wikipedia.org/wiki/TCP_sequence_prediction_attack\"\u003eTCP hijacking\u003c/a\u003e and spoofing attacks. Therefore, by default during nat my source port is randomized.\u003c/p\u003e\n\u003cp\u003eNow the game consoles will not tell us directly that this is the issue. Instead it will provide a \u0026ldquo;score\u0026rdquo; which is supposed to establish a rough sense of how easy it will be to establish a P2P connection.\u003c/p\u003e\n\u003cp\u003eNintendo Switch: Settings -\u0026gt; Internet -\u0026gt; Test Connection\u003c/p\u003e\n\u003cp\u003ePS5: Settings -\u0026gt; Network -\u0026gt; Test Internet Connection\u003c/p\u003e\n\u003cp\u003eBefore applying any changes, on the switch I had a \u0026ldquo;D\u0026rdquo; score on on the PS5 it was \u0026ldquo;Type 3\u0026rdquo;. I wish I can tell you what these scores mean, but the documentation is lacking to say the least\u0026hellip;\u003c/p\u003e\n\u003cp\u003eHowever, I was finally able to determine that nat source port randomization was the problem after stumbling upon this \u003ca href=\"https://www.reddit.com/r/OPNsenseFirewall/comments/g3sx2l/tip_opnsense_and_nintendo_switch_nat_rules/\"\u003eReddit thread\u003c/a\u003e \u0026ndash; which now takes us to the solution:\u003c/p\u003e\n\u003ch2 id=\"the-solution\"\u003eThe Solution\u003c/h2\u003e\n\u003cp\u003eWe need to tell our router that our gaming devices are special, and therefore does not need the additional security measure of randomizing the source port during nat. That way when the game is establishing a P2P connection, the ports aren\u0026rsquo;t randomized and the connection can proceed smoothly.\u003c/p\u003e\n\u003cp\u003eOn Opnsense, we can change this setting by navigating to Firewall -\u0026gt; NAT -\u0026gt; Outbound.\u003c/p\u003e\n\u003cp\u003eNow I do want this security measure to be applied to the rest of my devices. Therefore, we\u0026rsquo;ll be setting the mode to \u0026ldquo;Hybrid outbound NAT rule generation\u0026rdquo;  which allows us to specify the custom rules.\u003c/p\u003e\n\u003cp\u003eFrom there, we can add a manual rule for each game console with the following:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eInterface: WAN\u003c/li\u003e\n\u003cli\u003eTCP/IP Version: IPv4\u003c/li\u003e\n\u003cli\u003eProtocol: UDP\u003c/li\u003e\n\u003cli\u003eSource Address: Single host or network\n\u003cul\u003e\n\u003cli\u003eInsert Switch/PS5 address\u003c/li\u003e\n\u003cli\u003eReplace netmask with 32\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eSource Port: any\u003c/li\u003e\n\u003cli\u003eDestination Address: any\u003c/li\u003e\n\u003cli\u003eDestination Port: Any\u003c/li\u003e\n\u003cli\u003eStatic Port: \u003cstrong\u003eChecked\u003c/strong\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThe last option is what tells Opnsense to not randomize the source port.\u003c/p\u003e\n\u003cp\u003eFrom there, we can save, apply our settings, and rerun our connection tests. With this change, my Switch reports a NAT type of B and PS5 reports Type 2. Good enough for online gaming :)\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eSorry Xbox folks, I do not own one so I cannot tell you if this technique works for that platform as well.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2025.12.23",
    "tags": ["Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/fedora-coreos-first-impressions/",
    "url": "https://brandonrozek.com/blog/fedora-coreos-first-impressions/",
    "title": "Fedora CoreOS: First Impressions",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI have a VPS whose contract ends in December. Instead of renewing, I decided to switch providers of that VPS to OVHCloud. The latter\u0026rsquo;s commitment to \u003ca href=\"https://corporate.ovhcloud.com/en/sustainability/environment/\"\u003esustainability\u003c/a\u003e through renewables and component reuse is super cool. Now, I could\u0026rsquo;ve kept the migration simple and keep the configuration the same. I don\u0026rsquo;t write about it much here, but I have \u003ca href=\"https://www.redhat.com/en/ansible-collaborative\"\u003eAnsible\u003c/a\u003e playbooks for all my servers. However, \u003ca href=\"https://www.zdnet.com/article/what-is-immutable-linux-heres-why-youd-run-an-immutable-linux-distro/\"\u003eimmutable Linux distributions\u003c/a\u003e have been receiving a lot of attention over the past few years and tragically I knew little about them.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWhat is an immutable Linux distribution?\u003c/strong\u003e It is a Linux distribution with a read-only core. This prevents accidental modifications which overtime lead to an unstable system.\u003c/p\u003e\n\u003cp\u003eThere are \u003ca href=\"https://nixos.org/\"\u003emany\u003c/a\u003e \u003ca href=\"https://ubuntu.com/core\"\u003edifferent\u003c/a\u003e \u003ca href=\"https://microos.opensuse.org/\"\u003eoptions\u003c/a\u003e for these immutable distributions, but as you can see from the title of this post, I went with \u003ca href=\"https://www.fedoraproject.org/coreos/\"\u003eFedora CoreOS\u003c/a\u003e. The reason is simple. All my other servers run Fedora Server, so hopefully the changes I need to make to my playbooks are minimal.\u003c/p\u003e\n\u003cp\u003eAt the time of writing, Fedore CoreOS uses \u003ca href=\"https://ostreedev.github.io/ostree/introduction/\"\u003eOSTree\u003c/a\u003e to perform upgrades over the entire filesystem. These upgrades are \u003cem\u003eatomic\u003c/em\u003e which suggests that we are not updating individual packages but the entire Linux distribution as a whole. Apart from the read-only core, there are two writable directories \u003ccode\u003e/etc\u003c/code\u003e and \u003ccode\u003e/var\u003c/code\u003e. In fact, your home directory lives in \u003ccode\u003e/var/home\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"initial-installation\"\u003eInitial Installation\u003c/h2\u003e\n\u003cp\u003eOVHCloud does not have a way to upload an ISO and boot directly from there. Instead, we\u0026rsquo;ll have to use their rescue mode feature. Luckily, Timothée wrote up a \u003ca href=\"https://tim.siosm.fr/blog/2025/09/14/fedora-coreos-ovhcloud-vps/\"\u003egreat guide\u003c/a\u003e on how to get started. If this doesn\u0026rsquo;t exactly match your situation, the \u003ca href=\"https://docs.fedoraproject.org/en-US/fedora-coreos/getting-started/\"\u003eofficial documentation\u003c/a\u003e has over 20 different provisioning guides.\u003c/p\u003e\n\u003cp\u003eWhen I was following the documentation, one of the parts I got tripped up on was how much configuration to put in my Butane file. As noted above, I already have Ansible setup to copy over various files I need. Then I saw that \u003cem\u003eignition runs only once during the first boot of the system\u003c/em\u003e. Hence, if we wanted to customize our storage partitions or lay out networking, then this is a good place to do this. Otherwise, if we want to setup \u003ccode\u003esystemd\u003c/code\u003e services and the like, we can always add those via Ansible later.\u003c/p\u003e\n\u003cp\u003eIn other words, if you\u0026rsquo;re fine with the defaults and there\u0026rsquo;s a DHCP server running on your network, then the  \u003cem\u003ebase Butane config outlined in the documentation is sufficient\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eFrom the official documentation (write this to a file such as \u003ccode\u003econfig.bu\u003c/code\u003e):\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003evariant\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003efcos\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eversion\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1.6.0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003epasswd\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eusers\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#f92672\"\u003ename\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ecore\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003essh_authorized_keys\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        - \u003cspan style=\"color:#ae81ff\"\u003essh-rsa AAAA...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhere you replace the \u003ccode\u003essh-rsa\u003c/code\u003e line with your own SSH public key file.\u003c/p\u003e\n\u003cp\u003eThen, in order to get the ignition file, we can run the following command:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epodman run --interactive --rm quay.io/coreos/butane:release --pretty --strict \u0026lt; config.bu \u0026gt; config.ign\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"running-software\"\u003eRunning Software\u003c/h2\u003e\n\u003cp\u003eFor the most part, using OSTree to install additional packages is \u003ca href=\"https://docs.fedoraproject.org/en-US/fedora-coreos/faq/#_how_do_i_run_custom_applications_on_fedora_coreos\"\u003ehighly discouraged\u003c/a\u003e. Instead, it\u0026rsquo;s suggested to install and run things through \u003ca href=\"https://docs.fedoraproject.org/en-US/fedora-coreos/running-containers/\"\u003econtainers\u003c/a\u003e. The official documentation shows how to set up containers via the Butane configuration above, however, I kept my file as simple as shown above.  Instead since \u003ccode\u003e/etc/containers/systemd\u003c/code\u003e is writable, I wrote \u003ca href=\"https://brandonrozek.com/blog/migrating-docker-compose-podman-quadlets/\"\u003ePodman Quadlet files\u003c/a\u003e directly (\u003ca href=\"https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html\"\u003eOfficial Quadlet Documentation\u003c/a\u003e).\u003c/p\u003e\n\u003cp\u003eFor example, here is my Wireguard Quadlet file\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eWireGuard VPN Container\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAfter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enetwork-online.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWants\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enetwork-online.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Container]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eImage\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edocker.io/linuxserver/wireguard:latest\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eContainerName\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ewireguard\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Give the container the ability to add network interfaces\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAddCapability\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eNET_ADMIN\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Have the wireguard network accessible on the host\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eNetwork\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ehost\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Mount the WireGuard configuration directory\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eVolume\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/etc/wireguard:/config/wg_confs:Z\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRestart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ealways\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eTimeoutStartSec\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e900\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target default.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe following are my Ansible tasks which copy that file over to the machine.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e- \u003cspan style=\"color:#f92672\"\u003ename\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eCreate /etc/wireguard directory\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003ebecome\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eansible.builtin.file\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003epath\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e/etc/wireguard\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003estate\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003edirectory\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003emode\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;0700\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eowner\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eroot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003egroup\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eroot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e- \u003cspan style=\"color:#f92672\"\u003ename\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eCopy Quadlet container file\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003ebecome\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eansible.builtin.copy\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003esrc\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eetc/containers/systemd/wireguard.container\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003edest\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e/etc/containers/systemd/wireguard.container\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003emode\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;0644\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eowner\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eroot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003egroup\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eroot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eregister\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ewireguardcontainer\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e- \u003cspan style=\"color:#f92672\"\u003ename\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eReload systemd daemon to pick up Quadlet\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003ebecome\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eansible.builtin.systemd\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003edaemon_reload\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eyes\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003ewhen\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ewireguardcontainer.changed\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow not everything deserves a spot in \u003ccode\u003e/etc/containers/systemd\u003c/code\u003e. Take \u003ccode\u003efastfetch\u003c/code\u003e for example.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-txt\" data-lang=\"txt\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e             .\u0026#39;,;::::;,\u0026#39;.                 core@toolbx\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e         .\u0026#39;;:cccccccccccc:;,.             ------------\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      .;cccccccccccccccccccccc;.          OS: Fedora Linux 43 (Toolbx Container Image) x86_64\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .:cccccccccccccccccccccccccc:.        Host: OpenStack Nova (19.3.2)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  .;ccccccccccccc;.:dddl:.;ccccccc;.      Kernel: Linux 6.17.1-300.fc43.x86_64\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e .:ccccccccccccc;OWMKOOXMWd;ccccccc:.     Uptime: 22 hours, 16 mins\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.:ccccccccccccc;KMMc;cc;xMMc;ccccccc:.    Packages: 366 (rpm)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e,cccccccccccccc;MMM.;cc;;WW:;cccccccc,    Shell: bash 5.3.0\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e:cccccccccccccc;MMM.;cccccccccccccccc:    Terminal: conmon\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e:ccccccc;oxOOOo;MMM000k.;cccccccccccc:    CPU: 6 x Intel Core (Haswell, no TSX) (6) @ 2.99 GHz\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecccccc;0MMKxdd:;MMMkddc.;cccccccccccc;    GPU: Cirrus Logic GD 5446\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eccccc;XMO\u0026#39;;cccc;MMM.;cccccccccccccccc\u0026#39;    Memory: 920.56 MiB / 11.39 GiB (8%)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eccccc;MMo;ccccc;MMW.;ccccccccccccccc;     Swap: Disabled\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eccccc;0MNc.ccc.xMMd;ccccccccccccccc;      Disk (/): 11.53 GiB / 99.44 GiB (12%) - overlay\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecccccc;dNMWXXXWM0:;cccccccccccccc:,       Disk (/run/host/boot): 277.41 MiB / 349.87 MiB (79%) - ext4 [Read-only]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecccccccc;.:odl:.;cccccccccccccc:,.        Disk (/run/host/etc): 11.53 GiB / 99.44 GiB (12%) - xfs\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eccccccccccccccccccccccccccccc:\u0026#39;.          \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e:ccccccccccccccccccccccc:;,..             \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u0026#39;:cccccccccccccccc::;,.\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAll it does it displays system information. Now this is a very important task when showing off your system on Reddit, but it\u0026rsquo;s more of a \u003cem\u003esystem administration\u003c/em\u003e tool than a software service that your server provides. For these types of tools, we can use \u003ccode\u003etoolbox\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etoolbox create \u003cspan style=\"color:#75715e\"\u003e# Run this once\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etoolbox enter\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will give us a new prompt:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e⬢ [core@toolbx ~]$\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFrom here, we can use \u003ccode\u003ednf\u003c/code\u003e and treat it similarly as a mutable Fedora server machine.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dnf install fastfetch\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow you\u0026rsquo;ll notice that by default it mounts your home directory but the \u003ccode\u003e/etc\u003c/code\u003e and \u003ccode\u003e/var\u003c/code\u003e directories are those of the container. We can find all the files in the host system by accessing \u003ccode\u003e/run/host\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eI use this when trying to view my Nginx logs:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egoaccess /run/host/var/log/nginx/access.log\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"system-security\"\u003eSystem Security\u003c/h2\u003e\n\u003cp\u003eA huge benefit to running all your services via Quadlets is that Podman is able to automatically set the SELinux contexts and configure your firewall rules for you. This means that we don\u0026rsquo;t have to manually change the SELinux context of every file with \u003ccode\u003echcon\u003c/code\u003e. If you\u0026rsquo;re lazy, this means hopefully we don\u0026rsquo;t have to disable SELinux!\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSELinux:\u003c/strong\u003e Notice in my Wireguard Quadlet file I had the following\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eVolume\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/etc/wireguard:/config/wg_confs:Z\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe part after \u003ccode\u003ewg_confs:\u003c/code\u003e is an optional comma-separated list of options. Here are some that I found to be particularly relevant:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eOption\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eZ\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eLabel the content with a private unshared SELinux label.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003ez\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eLabel the content with a shared SELinux content label so that two or more containers can access it.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eU\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eRecursively change the owner and group of the source volume based on the UID and GID of the container\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003ero\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eThe container can only read, not write to the volume\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e\u003cstrong\u003eNFTables:\u003c/strong\u003e My other Fedora server systems use \u003ccode\u003efirewalld\u003c/code\u003e as the primary way I interface with the firewall. Instead of using a CLI tool, the idea is that we edit \u003ccode\u003e/etc/sysconfig/nftables.conf\u003c/code\u003e with the rules we want. I\u0026rsquo;m still getting used writing my firewall config this way, but I do like how it\u0026rsquo;s all easily viewable in one place.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s a version of what I have:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-nftables\" data-lang=\"nftables\"\u003e#!/usr/sbin/nft -f\n\n# Define the main table\ntable inet filter {\n    \n    # Data structure we\u0026#39;ll use to keep track of rate-limiting\n    set ssh_ratelimit {\n        type ipv4_addr\n        size 65536\n        flags dynamic,timeout\n        timeout 30s\n    }\n\n    chain input {\n        type filter hook input priority filter; policy drop;\n\n        # Allow loopback\n        iif lo accept\n\n        # Allow established/related connections\n        ct state established,related accept\n\n        # Allow ICMP (ping, etc)\n        ip protocol icmp accept\n        ip6 nexthdr icmpv6 accept\n\n        # SSH with rate limiting\n        tcp dport 22 ct state new limit rate over 12/minute burst 6 packets drop\n        tcp dport 22 accept\n\n        # Allow HTTP and HTTPS\n        tcp dport { 80, 443 } accept\n\n        # Drop everything else\n        drop\n    }\n\n    chain forward {\n        type filter hook forward priority filter; policy drop;\n        \n        # Allow established/related connections\n        ct state established,related accept\n    }\n\n    chain output {\n    \t# Allow outgoing connections\n        type filter hook output priority filter; policy accept;\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eI won\u0026rsquo;t go into detail how NFTables works here. Notice though how we don\u0026rsquo;t specify anything about the Podman network. As I said before, Podman will automatically handle that for us.\u003c/p\u003e\n\u003cp\u003eHowever, what Podman won\u0026rsquo;t automatically handle is if we try to change our NFTables configuration without a reboot. This is because reloading NFTables will \u003cem\u003ewipe the existing configuration\u003c/em\u003e. As such, we need to manually invoke Podman to regenerate the rules.\u003c/p\u003e\n\u003cp\u003eLuckily, we can override the NFTables systemd service so that it happens automatically. Create the file \u003ccode\u003e/etc/systemd/system/nftables.service.d/override.conf\u003c/code\u003e with the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStartPost\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003epodman network reload --all\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecReload\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003epodman network reload --all\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eThat wraps up the bits and pieces I had to learn while I was setting my machine up. From there, I\u0026rsquo;ve been running my CoreOS machine for a month and have not yet had any issues. It\u0026rsquo;s too early for me to make any grand claims, but it \u003cem\u003efeels\u003c/em\u003e incredibly reliable.\u003c/p\u003e\n\u003cp\u003eBy default, updates are automatic. Since these are atomic, it means that the machine reboots regularly to apply these updates. This encourages us to setup everything to survive reboots and not require any manual intervention. Also, boot times are quick with it clocking under 12 seconds for my VPS.\u003c/p\u003e\n\u003cp\u003eRunning everything in containers provides isolation between these services and the host. This is useful of course for security, but also allows everything to update on their own cadence and not conflict.\u003c/p\u003e\n\u003cp\u003eOverall, using an immutable distribution is different than traditional Linux server management. However hopefully with this setup, it incentivizes us to create less fragile systems. A community of maintainers will keep the stable read-only core in a great state, and if things go wrong, we can copy our container volumes to another machine.\u003c/p\u003e\n\u003cp\u003eSo if you haven\u0026rsquo;t already, I recommend giving Fedora CoreOS a shot. Next, I have to take a look at how it\u0026rsquo;s like using an immutable distribution on a desktop.\u003c/p\u003e\n",
    "date_published": "2025.11.28",
    "tags": ["Fedora CoreOS","Podman","SELinux","nftables"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/flattening-cases-avoid-nesting-lean-4/",
    "url": "https://brandonrozek.com/blog/flattening-cases-avoid-nesting-lean-4/",
    "title": "Flattening Cases to Avoid Nesting in Lean 4",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eNested cases in proofs increase cognitive load for the reader since they have to process not only the case recently stated but also all the case splits prior. That\u0026rsquo;s why if I can, I prefer to flatten out my cases so that we can see in one step all the variables we\u0026rsquo;re segmenting.\u003c/p\u003e\n\u003cp\u003eI came across this recently in Lean when working on Lattice proofs over integers with $\\infty$ and $-\\infty$  In Lean, we can define this \u0026ldquo;extended integer\u0026rdquo; (\u003ccode\u003eEInt\u003c/code\u003e) by using \u003ccode\u003eWithTop\u003c/code\u003e and \u003ccode\u003eWithBot\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003eimport Mathlib.Order.Interval.Basic\n\n-- An Integer with a top (∞) and bottom (-∞) element\ndef EInt : Type := WithBot (WithTop Int)\nderiving LinearOrder\n\n@[simp] def EInt.ninf : EInt := (⊥ : WithBot (WithTop Int))\n@[simp] def EInt.inf : EInt := (WithBot.some ⊤ : WithBot (WithTop Int))\n\nnotation \u0026#34;-∞\u0026#34; =\u0026gt; EInt.ninf\nnotation \u0026#34;∞\u0026#34; =\u0026gt; EInt.inf\n\n-- Helper instances so I can later write numbers and have them casted\ninstance: IntCast EInt where\n  intCast n := WithBot.some (WithTop.some n)\n\ninstance: OfNat EInt n where\n  ofNat := some (some (Int.ofNat n))\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eUnfortunately, using \u003ccode\u003eWithBot\u003c/code\u003e and \u003ccode\u003eWithTop\u003c/code\u003e is just weird. Look at how we would define functions and write proofs using them.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003edef EIntFun : EInt → ℤ\n  | none =\u0026gt; 0\n  | some ⊤ =\u0026gt; 0\n  | some (some _) =\u0026gt; 0\n\nlemma EIntFunIsZero (e: EInt) : EIntFun e = 0 := by\n  cases e\n  case none =\u0026gt;\n    rfl\n  case some e =\u0026gt;\n    cases e\n    case top =\u0026gt;\n      rfl\n    case coe e =\u0026gt;\n      rfl\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWhat\u0026rsquo;s more intuitive is to break it up based on whether it\u0026rsquo;s $-\\infty$, $\\infty$ or some integer $z$. Luckily, we\u0026rsquo;re able to define our own way of splitting up cases in Lean.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003edef EInt.casesOn.{u} {motive : EInt -\u0026gt; Sort u} (a : EInt)\n  (ninf : motive -∞)\n  (int : ∀ n : Int, motive ↑n)\n  (pinf : motive ∞) :\nmotive a := by\n  cases a\n  case none =\u0026gt;\n    exact ninf\n  case some v =\u0026gt;\n    cases v\n    case top =\u0026gt;\n      exact pinf\n    case coe n =\u0026gt;\n      exact int n\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWhen we\u0026rsquo;re doing a proof by cases, we\u0026rsquo;re trying to prove some \u003ccode\u003emotive a\u003c/code\u003e. What the above definition says, is that if we\u0026rsquo;re given some EInt \u003ccode\u003ea\u003c/code\u003e and three proofs regarding the motive of each of the cases, then performing the cases successfully proves the motive.\u003c/p\u003e\n\u003cp\u003eNotice how the proof now no longer has any nested cases:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003elemma EIntFunIsZero2 (e: EInt) : EIntFun e = 0 := by\n  cases e using EInt.casesOn\n  case ninf =\u0026gt;\n    rfl\n  case int z =\u0026gt;\n    rfl\n  case pinf =\u0026gt;\n    rfl\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIn fact, we can even use this trick to simplify our function\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003edef EIntFun2 (e: EInt) : ℤ := by\n  cases e using EInt.casesOn with\n  | ninf =\u0026gt;\n    exact 0\n  | int z =\u0026gt;\n    exact 0\n  | pinf =\u0026gt;\n    exact 0\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"a-more-complicated-example\"\u003eA more complicated example\u003c/h2\u003e\n\u003cp\u003ePersonally I find utility in defining how we want our cases to go prior to the proof itself. For example, let\u0026rsquo;s break up the domain further by considering the sign of our integers.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003edef EInt.casesOnSigns.{u} {motive : EInt -\u0026gt; Sort u} (a : EInt)\n  (ninf : motive -∞)\n  (nint : ∀ n : Int, n \u0026lt; 0 → motive ↑n)\n  (zero: ∀ n : Int, n = 0 → motive ↑n)\n  (pint : ∀ n : Int, n \u0026gt; 0 → motive ↑n)\n  (pinf : motive ∞) :\nmotive a := by\n  cases a\n  case none =\u0026gt;\n    exact ninf\n  case some v =\u0026gt;\n    cases v\n    case top =\u0026gt;\n      exact pinf\n    case coe n =\u0026gt;\n      by_cases n \u0026lt; 0\n      case pos H =\u0026gt;\n        exact nint n H\n      case neg H =\u0026gt;\n        by_cases n = 0\n        case pos H2 =\u0026gt;\n          exact zero n H2\n        case neg H2 =\u0026gt;\n          have H3 : n \u0026gt; 0 := by\n            have HH : n ≥ 0 := Int.not_lt.mp H\n            have HH2 : n ≠ 0 := H2\n            have HH3 : 0 ≠ n := Ne.symm HH2\n            exact lt_of_le_of_ne HH HH3\n          exact pint n H3\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe more complicated we make our cases, the more Lean will struggle to establish definitional equality. I personally find it useful to create helper lemmas for each of the cases to later help establish our motive.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eNegative Infinity Case\u003c/strong\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003e@[simp]\nlemma EInt.casesOnSigns.is_ninf.{u} {motive : EInt -\u0026gt; Sort u}\n  (ninf : motive -∞)\n  (nint : ∀ n : Int, n \u0026lt; 0 → motive ↑n)\n  (zero: ∀ n : Int, n = 0 → motive ↑n)\n  (pint : ∀ n : Int, n \u0026gt; 0 → motive ↑n)\n  (pinf : motive ∞) : EInt.casesOnSigns (-∞) ninf nint zero pint pinf = ninf := rfl\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe above says that if we perform a cases on $-\\infty$ then the result will be equivalent to the \u003ccode\u003eninf\u003c/code\u003e case.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eNegative Integer Case\u003c/strong\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003elemma EInt.casesOnSigns.is_nint.{u} {motive : EInt -\u0026gt; Sort u}\n  (z : Int)\n  (Hz : z \u0026lt; 0)\n  (ninf : motive -∞)\n  (nint : ∀ n : Int, n \u0026lt; 0 → motive ↑n)\n  (zero: ∀ n : Int, n = 0 → motive ↑n)\n  (pint : ∀ n : Int, n \u0026gt; 0 → motive ↑n)\n  (pinf : motive ∞) :\n  EInt.casesOnSigns (↑z) ninf nint zero pint pinf = nint z Hz := by\n  unfold casesOnSigns\n  show (casesOn (↑z) ninf (fun n =\u0026gt; if h : n \u0026lt; 0 then nint n h\n                                    else if h : n = 0 then zero n h\n                                    else pint n (by omega : n \u0026gt; 0)) pinf) = nint z Hz\n  change (if h : z \u0026lt; 0 then nint z h\n          else if h : z = 0 then zero z h\n          else pint z (by omega : z \u0026gt; 0)) = nint z Hz\n  rw [dif_pos Hz]\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWith our usage of inequalities, we have to help guide Lean through this proof. We first unfold the \u003ccode\u003ecasesOnSigns\u003c/code\u003e definition to get the goal shown in the \u003ccode\u003eshow\u003c/code\u003e tactic. From there, we already know that our EInt is the integer \u003ccode\u003ez\u003c/code\u003e, so we can simplify the cases to our nested if-then-else statement.  After that, since we have the hypothesis that our integer \u003ccode\u003ez\u003c/code\u003e is negative, we can directly get the \u003ccode\u003enint z h\u003c/code\u003e case from the consequent of the outer ite.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eZero Case\u003c/strong\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003e@[simp]\nlemma EInt.casesOnSigns.is_zero.{u}  {motive : EInt -\u0026gt; Sort u}\n  (z : Int)\n  (Hn : z = 0)\n  (ninf : motive -∞)\n  (nint : ∀ n : Int, n \u0026lt; 0 → motive ↑n)\n  (zero: ∀ n : Int, n = 0 → motive ↑n)\n  (pint : ∀ n : Int, n \u0026gt; 0 → motive ↑n)\n  (pinf : motive ∞) : (EInt.casesOnSigns (z) ninf nint zero pint pinf)  = zero z Hn := by\n    subst z\n    rfl\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eOnce we substitute zero in, Lean can automatically establish definitional equality.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePositive Case\u003c/strong\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003elemma EInt.casesOnSigns.is_pint.{u}  {motive : EInt -\u0026gt; Sort u}\n  (z : Int)\n  (Hz : z \u0026gt; 0)\n  (ninf : motive -∞)\n  (nint : ∀ n : Int, n \u0026lt; 0 → motive ↑n)\n  (zero: ∀ n : Int, n = 0 → motive ↑n)\n  (pint : ∀ n : Int, n \u0026gt; 0 → motive ↑n)\n  (pinf : motive ∞) : (EInt.casesOnSigns (z) ninf nint zero pint pinf)  = pint z Hz := by\n  unfold casesOnSigns\n  show (casesOn (↑z) ninf (fun n =\u0026gt; if h : n \u0026lt; 0 then nint n h\n                                    else if h : n = 0 then zero n h\n                                    else pint n (by omega : n \u0026gt; 0)) pinf) = pint z Hz\n  change (if h : z \u0026lt; 0 then nint z h\n          else if h : z = 0 then zero z h\n          else pint z (Hz : z \u0026gt; 0)) = pint z Hz\n  have HH: ¬(z \u0026lt; 0) := not_lt_of_gt Hz\n  rw [dif_neg HH]\n  have HH2: ¬(z = 0) := Int.ne_of_gt Hz\n  rw [dif_neg HH2]\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eSimilar to the negative case, but we need to do slightly more work to get to the last alternative within the nested if-then-else statement.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eInfinity Case\u003c/strong\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003e@[simp]\nlemma EInt.casesOnSigns.is_pinf.{u} {motive : EInt -\u0026gt; Sort u}\n  (ninf : motive -∞)\n  (nint : ∀ n : Int, n \u0026lt; 0 → motive ↑n)\n  (zero: ∀ n : Int, n = 0 → motive ↑n)\n  (pint : ∀ n : Int, n \u0026gt; 0 → motive ↑n)\n  (pinf : motive ∞) : EInt.casesOnSigns (∞) ninf nint zero pint pinf = pinf := rfl\n\u003c/code\u003e\u003c/pre\u003e\u003chr\u003e\n\u003cp\u003eLike before, now that we have our new definition \u003ccode\u003eEInt.casesOnSigns\u003c/code\u003e, we can create our function which determines whether an EInt is positive cleanly.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003edef EInt.isPos (e: EInt) : Bool := by\n  cases e using EInt.casesOnSigns with\n  | ninf =\u0026gt; exact false\n  | nint _ =\u0026gt; exact false\n  | zero =\u0026gt; exact false\n  | pint _ =\u0026gt; exact true\n  | pinf =\u0026gt; exact true\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis is much better than if we worked with the original \u003ccode\u003eWithTop\u003c/code\u003e and \u003ccode\u003eWithBot\u003c/code\u003e version! Now let\u0026rsquo;s prove that our EInt is positive iff it is greater than zero. To do this we\u0026rsquo;ll need one helper lemma.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003elemma EInt.coe_lt_coe {a b : Int}:  ((↑a : EInt) \u0026lt; (↑b: EInt)) ↔ a \u0026lt; b := by\n  have H1 : ((↑a : EInt) \u0026lt; (↑b: EInt)) → a \u0026lt; b := by\n    intro (H: (↑a : EInt) \u0026lt; (↑b: EInt))\n    apply WithTop.coe_lt_coe.mp\n    exact WithBot.coe_lt_coe.mp H\n  have H2 : a \u0026lt; b → ((↑a : EInt) \u0026lt; (↑b: EInt)) := by\n    clear H1\n    intro (H : a \u0026lt; b)\n    apply WithBot.coe_lt_coe.mpr\n    exact WithTop.coe_lt_coe.mpr H\n  exact Iff.intro H1 H2\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe above lemma states that if the integer $a$ is less than the integer $b$, then the EInt version of $a$ is less than the EInt version of $b$ and vice versa.  Now for the main proof\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSoundness\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eIf our EInt \u003ccode\u003ee\u003c/code\u003e is positive, then \u003ccode\u003ee\u003c/code\u003e is greater than zero.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003elemma EInt.isPos_sound (e: EInt) : e.isPos = true → e \u0026gt; 0 := by\n  intro H\n  cases e using EInt.casesOnSigns\n  case ninf =\u0026gt;\n    contradiction\n  case nint n Hn =\u0026gt;\n    unfold EInt.isPos at H\n    rw [EInt.casesOnSigns.is_nint n Hn] at H\n    contradiction\n  case zero n Hz =\u0026gt;\n    unfold EInt.isPos at H\n    rw [EInt.casesOnSigns.is_zero n Hz] at H\n    contradiction\n  case pint n Hp =\u0026gt;\n    apply EInt.coe_lt_coe.mpr Hp\n  case pinf =\u0026gt;\n    exact Batteries.compareOfLessAndEq_eq_lt.mp rfl\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003eCompleteness\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eIf our EInt \u003ccode\u003ee\u003c/code\u003e is greater than zero, then \u003ccode\u003ee\u003c/code\u003e is positive.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003elemma EInt.isPos_complete (e: EInt) : e \u0026gt; 0 → e.isPos = true := by\n  intro H\n  cases e using EInt.casesOnSigns\n  case ninf =\u0026gt;\n    contradiction\n  case nint n Hn =\u0026gt;\n    have H2 : n \u0026gt; 0 := EInt.coe_lt_coe.mp H\n    have H3 : ¬(n \u0026gt; 0) := not_lt_of_gt Hn\n    contradiction\n  case zero n Hz =\u0026gt;\n    have H2 : n \u0026gt; 0 := EInt.coe_lt_coe.mp H\n    have H3 : ¬(n \u0026gt; 0) := Eq.not_gt Hz\n    contradiction\n  case pint n Hp =\u0026gt;\n    unfold EInt.isPos\n    rw [EInt.casesOnSigns.is_pint n Hp _ _ _ _ _ ]\n  case pinf =\u0026gt;\n    unfold EInt.isPos\n    rw [EInt.casesOnSigns.is_pinf]\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2025.10.05",
    "tags": ["Lean","Proof assistant","Formal Proof"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/cursed-knowledge-javascript-arrays-are-objects/",
    "url": "https://brandonrozek.com/blog/cursed-knowledge-javascript-arrays-are-objects/",
    "title": "Cursed Knowledge: Javascript Arrays Are Objects",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eMy friend Ethan recently wrote a blog post on \u003ca href=\"https://emar10.dev/posts/cursed-commands-part-1/\"\u003ecursed commands\u003c/a\u003e. Chris shared with me that Immich has a page on their site called \u003ca href=\"https://immich.app/cursed-knowledge/\"\u003ecursed knowledge\u003c/a\u003e, and it looks like this has started a trend. I\u0026rsquo;ve seen my fair share of the dark arts in programming, so I\u0026rsquo;ll hop on and share what I know about JavaScript arrays.\u003c/p\u003e\n\u003cp\u003eJavaScript arrays are \u003ca href=\"https://262.ecma-international.org/#sec-array-exotic-objects\"\u003e\u003cem\u003eexotic objects\u003c/em\u003e\u003c/a\u003e according to the ECMAScript specification. Therefore, they may lead to unintuitive behavior if we think of these arrays as C-like.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s play around.\u003c/p\u003e\n\u003ch3 id=\"concept-1-javascript-arrays-are-not-continguous\"\u003eConcept 1: JavaScript arrays are not continguous\u003c/h3\u003e\n\u003cp\u003eFirst, consider the following array:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003elet\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e];\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAs one might expect, \u003ccode\u003ex.length\u003c/code\u003e is equal to \u003ccode\u003e3\u003c/code\u003e. To tell whether or not an index is in an array, we can use the \u003ccode\u003ein\u003c/code\u003e operator.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ein\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// Evaluates to false\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf we try to access the 3rd index, the result will evaluate to \u003ccode\u003eundefined\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e] \u003cspan style=\"color:#75715e\"\u003e// Evaluates to undefined\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow let\u0026rsquo;s assign an element to the 4th index. Keep in mind that we\u0026rsquo;re skipping over the 3rd one.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow when we check our \u003ccode\u003elength\u003c/code\u003e property, it\u0026rsquo;ll say that our array is now of size \u003ccode\u003e5\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// Evaluates to 5\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHowever, the 3rd index still does not exist\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ein\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// Evaluates to false\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"concept-2-explicit-vs-implicit-undefined\"\u003eConcept 2: Explicit vs Implicit \u003ccode\u003eundefined\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eRecall that \u003ccode\u003ex[3]\u003c/code\u003e evaluates to \u003ccode\u003eundefined\u003c/code\u003e. What happens when we set the value explicitly?\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eundefined\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ein\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// Evaluates to true\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSo there is a difference on whether we have explicitly set an index to \u003ccode\u003eundefined\u003c/code\u003e! This distinction is not always used. For example, our trusty for-of loop does not care.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eof\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ea\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWill print out\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e0\n1\n2\nundefined\n4\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"concept-3-indices-are-actually-strings\"\u003eConcept 3: Indices are actually strings\u003c/h3\u003e\n\u003cp\u003eGiven that we have a length property and that we\u0026rsquo;ve been indexing with numeric keys, it must mean that arrays have numeric indices. Right?\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e\u0026#34;0\u0026#34; in [\u0026#34;a\u0026#34;, \u0026#34;b\u0026#34;] // Evaluates to true\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eOkay, it looks like there\u0026rsquo;s some conversion magic that\u0026rsquo;s happening behind the scenes here. The ECMAScript specification says that an array index must be strictly less than $2^{32}$. So what happens if it is not?\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003elet\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e4294967296\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// Evaluates to [ 0, \u0026#39;4294967296\u0026#39;: true ]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIt looks like it no longer gets treated as an array item, but instead treats it as an arbitrary key-value pair. Why stop there, this must mean that we can store any sort of arbitrary data in our array.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// Evaluates to [ 0, \u0026#39;4294967296\u0026#39;: true, name: \u0026#39;Brandon\u0026#39; ]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"viewing-arrays-as-objects\"\u003eViewing arrays as objects\u003c/h3\u003e\n\u003cp\u003eNow everything starts to make more sense when we think of these arrays as objects.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-javscript\" data-lang=\"javscript\"\u003elet x = [0, 1, 2];\nx[4] = 4;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eInternally, this corresponds to the object:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;0\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;1\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;4\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFrom this object, we can see that the keys are strings and that the 3rd key is not in the object. Now let\u0026rsquo;s see what happens when we explicitly set \u003ccode\u003ex[5] = undefined\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;0\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;1\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;4\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;5\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eundefined\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe 5th key is now in our object and it\u0026rsquo;s set to an undefined value. We also get an undefined value when we try to retrieve a value of a key that is not in our object.\u003c/p\u003e\n\u003cp\u003eThe length of our array is the highest \u0026ldquo;numeric\u0026rdquo; key within our object (subject to the size limit). When we iterate over our array using \u003ccode\u003efor-of\u003c/code\u003e, we\u0026rsquo;re iterating from \u003ccode\u003e\u0026quot;0\u0026quot;\u003c/code\u003e to our length.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eof\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ea\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIs the same as:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThat\u0026rsquo;s an exotic object for you.\u003c/p\u003e\n",
    "date_published": "2025.09.01",
    "tags": ["JavaScript"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/deterministic-set-iteration-dafny/",
    "url": "https://brandonrozek.com/blog/deterministic-set-iteration-dafny/",
    "title": "Deterministically Iterating over a set within Dafny functions",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eSay we have a set that we want to iterate over within a pure Dafny function. For sake of example, we will look at a set of strings. In Dafny,  \u003ccode\u003evar x :| condition\u003c/code\u003e denotes \u0026ldquo;let us define variable x such that [condition]\u0026rdquo;. Therefore, a first attempt at writing our function might be:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efunction iterate_helper(collection: set\u0026lt;string\u0026gt;, acc: seq\u0026lt;string\u0026gt;): seq\u0026lt;string\u0026gt;\n{\n    if collection == {} then acc\n    else\n        var x :| x in collection;\n        var newAcc := acc + [x];\n        var newCollection := collection - {x};\n        iterate_helper(newCollection, newAcc)\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe issue is that Dafny will complain with the following error message:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eto be compilable, the value of a let-such-that expression must be uniquely determined\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eDafny functions must be deterministic. This means that no matter how many times we call a function with some specified input, we will always get the same output. Therefore, as the error message suggests, we need to write a condition that \u003cem\u003euniquely\u003c/em\u003e determines \u003ccode\u003ex\u003c/code\u003e. One way to achieve this is to specify an order as described in \u003ca href=\"https://www.microsoft.com/en-us/research/wp-content/uploads/2016/12/krml252.pdf\"\u003eRustan\u0026rsquo;s paper\u003c/a\u003e:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efunction iterate_helper(collection: set\u0026lt;string\u0026gt;, acc: seq\u0026lt;string\u0026gt;): seq\u0026lt;string\u0026gt;\n{\n    if collection == {} then acc\n    else\n        var x :| x in collection \u0026amp;\u0026amp; forall y | y in collection :: x \u0026lt;= y;\n        var newAcc := acc + [x];\n        var newCollection := collection - {x};\n        iterate_helper(newCollection, newAcc)\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eUnfortunately, this code will return two errors:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003ecannot establish the existence of LHS values that satisfy the such-that predicate\u003c/p\u003e\u003c/blockquote\u003e\n\u003cblockquote\u003e\n\u003cp\u003eto be compilable, the value of a let-such-that expression must be uniquely determined\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eHowever, it\u0026rsquo;s totally possible to define an ordering over strings. We\u0026rsquo;ll just need to do some work to convince the verifier of this.\u003c/p\u003e\n\u003ch3 id=\"comparing-two-strings\"\u003eComparing two strings\u003c/h3\u003e\n\u003cp\u003eSince strings in Dafny are a sequence of characters, it turns out that the \u003ccode\u003e\u0026lt;=\u003c/code\u003e relation checks whether the left side is a prefix of the right side. Therefore, there is no such thing as a \u003cem\u003eunique\u003c/em\u003e minimum element, since there are incomparable elements like \u0026ldquo;a\u0026rdquo; and \u0026ldquo;b\u0026rdquo;.\u003c/p\u003e\n\u003cp\u003eAs such, our first step is to create a less than or equal to (\u003ccode\u003e\u0026lt;=\u003c/code\u003e) relation that induces a total order.  Consider the following function that determines the order based on the left-most character.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-function\" data-lang=\"function\"\u003efunction string_le(s1: string, s2: string): bool\n    decreases |s1| + |s2|\n{\n    if |s1| == 0 \u0026amp;\u0026amp; |s2| \u0026gt; 0 then\n        true\n    else if |s1| \u0026gt; 0 \u0026amp;\u0026amp; |s2| == 0 then\n        false\n    else if |s1| == 0 \u0026amp;\u0026amp; |s2| == 0 then\n        true\n    else\n        assert(|s1| \u0026gt; 0);\n        assert(|s2| \u0026gt; 0);\n        var c1 := s1[0];\n        var c2 := s2[0];\n        if c1 \u0026lt; c2 then\n            true\n        else if c1 \u0026gt; c2 then\n            false\n        else\n            string_le(s1[1..], s2[1..])\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"properties-of-our-comparison-function\"\u003eProperties of our comparison function\u003c/h3\u003e\n\u003cp\u003eFrom here we need to prove that our relation induces a total order. For this, we need to show that it is reflexive, total, anti-symmetric, and transitive.\u003c/p\u003e\n\u003cp\u003eReflexivity: All strings are less than or equal to themselves\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003elemma string_le_reflexive()\n    ensures forall s :: string_le(s ,s)\n{\n    forall s ensures string_le(s, s)\n    {\n        string_le_reflexive_helper(s);\n    }\n}\n\nlemma string_le_reflexive_helper(s1: string)\n    ensures string_le(s1, s1)\n{}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTotality: Given two strings, one is less than or equal to the other.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003elemma string_le_totality()\n    ensures forall s1, s2 :: string_le(s1, s2) || string_le(s2, s1)\n{\n    forall s1, s2 ensures string_le(s1, s2) || string_le(s2, s1)\n    {\n        string_le_totality_helper(s1, s2);\n    }\n}\n\nlemma string_le_totality_helper(s1: string, s2: string)\n    ensures string_le(s1, s2) || string_le(s2, s1)\n{}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAntisymmetric: If one string is less than or equal to another string and that other string is also less than or equal to the original string then both strings are equivalent.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003elemma string_le_antisymmetric()\n    ensures forall s1, s2 :: string_le(s1, s2) \u0026amp;\u0026amp; string_le(s2, s1) ==\u0026gt; s1 == s2\n{\n    forall s1, s2 | string_le(s1, s2) \u0026amp;\u0026amp; string_le(s2, s1)\n    ensures s1 == s2\n    {\n        string_le_antisymmetric_helper(s1, s2);\n    }\n}\n\nlemma string_le_antisymmetric_helper(s1: string, s2: string)\n    requires string_le(s1, s2)\n    requires string_le(s2, s1)\n    ensures s1 == s2\n{}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTransitive: If one string is less than or equal to another string, and that other string is less than or equal to some third string, then the first string is less than or equal to that third string.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003elemma string_le_transitive()\n    ensures forall s1, s2, s3 :: string_le(s1, s2) \u0026amp;\u0026amp; string_le(s2, s3) ==\u0026gt; string_le(s1, s3)\n{\n    forall s1, s2, s3 | string_le(s1, s2) \u0026amp;\u0026amp; string_le(s2, s3)\n    ensures string_le(s1, s3)\n    {\n        string_le_transitive_helper(s1, s2, s3);\n    }\n}\n\nlemma string_le_transitive_helper(s1: string, s2: string, s3: string)\n    requires string_le(s1, s2)\n    requires string_le(s2, s3)\n    ensures string_le(s1, s3)\n{}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThen, we conviniently package all the properties together:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003elemma string_le_properties()\n    ensures forall s :: string_le(s, s)\n    ensures forall s1, s2 :: string_le(s1, s2) \u0026amp;\u0026amp; string_le(s2, s1) ==\u0026gt; s1 == s2\n    ensures forall s1, s2, s3 :: string_le(s1, s2) \u0026amp;\u0026amp; string_le(s2, s3) ==\u0026gt; string_le(s1, s3)\n    ensures forall s1, s2 :: string_le(s1, s2) || string_le(s2, s1)\n{\n    string_le_reflexive();\n    string_le_antisymmetric();\n    string_le_transitive();\n    string_le_totality();\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"string-sets-have-a-minimum\"\u003eString sets have a minimum\u003c/h3\u003e\n\u003cp\u003eWith the total ordering of strings, we can prove that a smallest element exists within a non-empty set \u003ccode\u003es\u003c/code\u003e. First, let us invoke our comparison properties lemma, so the verifier has access to those properties:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003estring_le_properties();\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eSince our set is non-empty, we can grab an arbitrary element from \u003ccode\u003es\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003evar x :| x in s;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFor this proof, we\u0026rsquo;ll approach it inductively. First, let\u0026rsquo;s consider when \u003ccode\u003es == {x}\u003c/code\u003e.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eBy construction, every element in \u003ccode\u003es\u003c/code\u003e is equal to \u003ccode\u003ex\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eThen by reflexivity, \u003ccode\u003ex\u003c/code\u003e is smaller than every element in \u003ccode\u003es\u003c/code\u003e.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eassert forall y :: y in s ==\u0026gt; y == x;\nassert forall y :: y in s ==\u0026gt; string_le(x, y);\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow let\u0026rsquo;s consider the inductive case. The set in this case has more elements than just \u003ccode\u003ex\u003c/code\u003e. Let\u0026rsquo;s consider \u003ccode\u003es'\u003c/code\u003e the subset of \u003ccode\u003es\u003c/code\u003e without the element \u003ccode\u003ex\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003evar s\u0026#39; := s - {x};\nassert s\u0026#39; != {};\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eSince \u003ccode\u003es'\u003c/code\u003e is non-empty, we can by induction say that  \u003ccode\u003es'\u003c/code\u003e has a smallest element.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003estring_smallest_exists(s\u0026#39;);\nvar x\u0026#39; :| x\u0026#39; in s\u0026#39; \u0026amp;\u0026amp; forall y :: y in s\u0026#39; ==\u0026gt; string_le(x\u0026#39;, y);\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAs \u003ccode\u003es'\u003c/code\u003e is the subset of \u003ccode\u003es\u003c/code\u003e without \u003ccode\u003ex\u003c/code\u003e, we can assert that \u003ccode\u003ex'\u003c/code\u003e and \u003ccode\u003ex\u003c/code\u003e are not the same:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eassert x\u0026#39; != x;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFrom here, we compare both \u003ccode\u003ex\u003c/code\u003e and \u003ccode\u003ex'\u003c/code\u003e (which we\u0026rsquo;re able to do since \u003ccode\u003e\u0026lt;=\u003c/code\u003e is total)\u003c/p\u003e\n\u003cp\u003eCase 1: \u003ccode\u003ex \u0026lt;= x'\u003c/code\u003e: By transitivity, \u003ccode\u003ex\u003c/code\u003e will be less than all the elements of \u003ccode\u003es'\u003c/code\u003e. Since \u003ccode\u003es'\u003c/code\u003e is  \u003ccode\u003es\u003c/code\u003e  without \u003ccode\u003ex\u003c/code\u003e, we can safely say that \u003ccode\u003ex\u003c/code\u003e is less than every element in \u003ccode\u003es\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eassert forall y :: y in s\u0026#39; ==\u0026gt; string_le(x, y);\nassert forall y :: y in s ==\u0026gt; string_le(x, y);\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eCase 2: \u003ccode\u003e!(x \u0026lt;= x')\u003c/code\u003e. From totality, we have that \u003ccode\u003ex' \u0026lt;= x\u003c/code\u003e. Since we know from the inductive hypothesis that \u003ccode\u003ex'\u003c/code\u003e is the minimum of \u003ccode\u003es'\u003c/code\u003e and \u003ccode\u003es\u003c/code\u003e is \u003ccode\u003es'\u003c/code\u003e with the element \u003ccode\u003ex\u003c/code\u003e, we can conclude that \u003ccode\u003ex'\u003c/code\u003e is the smallest element of \u003ccode\u003es\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eassert !string_le(x, x\u0026#39;);\nassert string_le(x\u0026#39;, x);\nassert forall y :: y in s ==\u0026gt; string_le(x\u0026#39;, y);\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWith that, we\u0026rsquo;ve proven that a smallest string exists! Here\u0026rsquo;s the lemma in its entirety:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003elemma string_smallest_exists(s: set\u0026lt;string\u0026gt;)\n    requires s != {}\n    decreases s\n    ensures exists x :: x in s \u0026amp;\u0026amp; forall y :: y in s ==\u0026gt; string_le(x, y)\n{\n    string_le_properties();\n\n    var x :| x in s;\n\n    // Base Case\n    if s == {x} {\n        assert forall y :: y in s ==\u0026gt; y == x;\n        assert forall y :: y in s ==\u0026gt; string_le(x, y);\n\t\n    // Inductive Case\n    } else {\n        var s\u0026#39; := s - {x};\n        assert s\u0026#39; != {};\n        string_smallest_exists(s\u0026#39;);\n        var x\u0026#39; :| x\u0026#39; in s\u0026#39; \u0026amp;\u0026amp; forall y :: y in s\u0026#39; ==\u0026gt; string_le(x\u0026#39;, y);\n        assert x != x\u0026#39;;\n\n        if string_le(x, x\u0026#39;) {\n            assert forall y :: y in s\u0026#39; ==\u0026gt; string_le(x, y);\n            assert forall y :: y in s ==\u0026gt; string_le(x, y);\n        } else {\n            // x\u0026#39; is smaller than x\n            assert !string_le(x, x\u0026#39;);\n            assert string_le(x\u0026#39;, x);\n            assert forall y :: y in s ==\u0026gt; string_le(x\u0026#39;, y);\n        }\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"select-the-smallest-element-from-a-set\"\u003eSelect the smallest element from a set\u003c/h3\u003e\n\u003cp\u003eNow that we have determined that a minimum exists in a set, we can use these lemmas to establish that we can uniquely determine the element that we select based on the ordering.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efunction select_string_from_set(collection: set\u0026lt;string\u0026gt;): string\n    requires collection != {}\n{\n    string_le_properties();\n    string_smallest_exists(collection);\n    var value :| value in collection \u0026amp;\u0026amp; forall y | y in collection :: string_le(value, y);\n    value\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"conclusion\"\u003eConclusion\u003c/h3\u003e\n\u003cp\u003eRevisiting our iteration example, we can use our new \u003ccode\u003eselect_string_from_set\u003c/code\u003e function to iterate over a set of strings in a pure deterministic function. More specifically, we\u0026rsquo;ll visit all the elements in the collection in the order defined by our relation.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efunction iterate_helper(collection: set\u0026lt;string\u0026gt;, acc: seq\u0026lt;string\u0026gt;): seq\u0026lt;string\u0026gt;\n    decreases collection\n{\n    if collection == {} then acc\n    else\n        var x := select_string_from_set(collection);\n        var newAcc := acc + [x];\n        var newCollection := collection - {x};\n        iterate_helper(newCollection, newAcc)\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFrom here you can generalize beyond a set of strings, as long as you\u0026rsquo;re able to prove the properties of a total order. The full code for our string sets example is below:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efunction string_le(s1: string, s2: string): bool\n    decreases |s1| + |s2|\n{\n    if |s1| == 0 \u0026amp;\u0026amp; |s2| \u0026gt; 0 then\n        true\n    else if |s1| \u0026gt; 0 \u0026amp;\u0026amp; |s2| == 0 then\n        false\n    else if |s1| == 0 \u0026amp;\u0026amp; |s2| == 0 then\n        true\n    else\n        assert(|s1| \u0026gt; 0);\n        assert(|s2| \u0026gt; 0);\n        var c1 := s1[0];\n        var c2 := s2[0];\n        if c1 \u0026lt; c2 then\n            true\n        else if c1 \u0026gt; c2 then\n            false\n        else\n            string_le(s1[1..], s2[1..])\n}\n\nlemma string_le_reflexive()\n    ensures forall s :: string_le(s ,s)\n{\n    forall s ensures string_le(s, s)\n    {\n        string_le_reflexive_helper(s);\n    }\n}\n\nlemma string_le_reflexive_helper(s1: string)\n    ensures string_le(s1, s1)\n{}\n\nlemma string_le_totality()\n    ensures forall s1, s2 :: string_le(s1, s2) || string_le(s2, s1)\n{\n    forall s1, s2 ensures string_le(s1, s2) || string_le(s2, s1)\n    {\n        string_le_totality_helper(s1, s2);\n    }\n}\n\nlemma string_le_totality_helper(s1: string, s2: string)\n    ensures string_le(s1, s2) || string_le(s2, s1)\n{}\n\nlemma string_le_antisymmetric()\n    ensures forall s1, s2 :: string_le(s1, s2) \u0026amp;\u0026amp; string_le(s2, s1) ==\u0026gt; s1 == s2\n{\n    forall s1, s2 | string_le(s1, s2) \u0026amp;\u0026amp; string_le(s2, s1)\n    ensures s1 == s2\n    {\n        string_le_antisymmetric_helper(s1, s2);\n    }\n}\n\nlemma string_le_antisymmetric_helper(s1: string, s2: string)\n    requires string_le(s1, s2)\n    requires string_le(s2, s1)\n    ensures s1 == s2\n{}\n\n\nlemma string_le_transitive()\n    ensures forall s1, s2, s3 :: string_le(s1, s2) \u0026amp;\u0026amp; string_le(s2, s3) ==\u0026gt; string_le(s1, s3)\n{\n    forall s1, s2, s3 | string_le(s1, s2) \u0026amp;\u0026amp; string_le(s2, s3)\n    ensures string_le(s1, s3)\n    {\n        string_le_transitive_helper(s1, s2, s3);\n    }\n}\n\nlemma string_le_transitive_helper(s1: string, s2: string, s3: string)\n    requires string_le(s1, s2)\n    requires string_le(s2, s3)\n    ensures string_le(s1, s3)\n{}\n\nlemma string_le_properties()\n    ensures forall s :: string_le(s, s)\n    ensures forall s1, s2 :: string_le(s1, s2) \u0026amp;\u0026amp; string_le(s2, s1) ==\u0026gt; s1 == s2\n    ensures forall s1, s2, s3 :: string_le(s1, s2) \u0026amp;\u0026amp; string_le(s2, s3) ==\u0026gt; string_le(s1, s3)\n    ensures forall s1, s2 :: string_le(s1, s2) || string_le(s2, s1)\n{\n    string_le_reflexive();\n    string_le_antisymmetric();\n    string_le_transitive();\n    string_le_totality();\n}\n\n\nlemma string_smallest_exists(s: set\u0026lt;string\u0026gt;)\n    requires s != {}\n    decreases s\n    ensures exists x :: x in s \u0026amp;\u0026amp; forall y :: y in s ==\u0026gt; string_le(x, y)\n{\n    string_le_properties();\n\n    var x :| x in s;\n\n    if s == {x} {\n        assert forall y :: y in s ==\u0026gt; y == x;\n        assert forall y :: y in s ==\u0026gt; string_le(x, y);\n    } else {\n        // For sets with more than one element, we use induction-like reasoning\n        var s\u0026#39; := s - {x};\n\n        assert s\u0026#39; != {};\n        string_smallest_exists(s\u0026#39;);\n\n        var x\u0026#39; :| x\u0026#39; in s\u0026#39; \u0026amp;\u0026amp; forall y :: y in s\u0026#39; ==\u0026gt; string_le(x\u0026#39;, y);\n        assert x != x\u0026#39;;\n\n        if string_le(x, x\u0026#39;) {\n            assert forall y :: y in s\u0026#39; ==\u0026gt; string_le(x, y);\n            assert forall y :: y in s ==\u0026gt; string_le(x, y);\n        } else {\n            // x\u0026#39; is smaller than x\n            assert !string_le(x, x\u0026#39;);\n            assert string_le(x\u0026#39;, x);\n            assert forall y :: y in s ==\u0026gt; string_le(x\u0026#39;, y);\n        }\n    }\n}\n\nfunction select_string_from_set(collection: set\u0026lt;string\u0026gt;): string\n    requires collection != {}\n{\n    string_le_properties();\n    string_smallest_exists(collection);\n    var value :| value in collection \u0026amp;\u0026amp; forall y | y in collection :: string_le(value, y);\n    value\n}\n\nfunction iterate_helper(collection: set\u0026lt;string\u0026gt;, acc: seq\u0026lt;string\u0026gt;): seq\u0026lt;string\u0026gt;\n    decreases collection\n{\n    if collection == {} then acc\n    else\n        var x := select_string_from_set(collection);\n        var newAcc := acc + [x];\n        var newCollection := collection - {x};\n        iterate_helper(newCollection, newAcc)\n}\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2025.07.06",
    "tags": ["Dafny","Formal methods","Deterministic algorithm","Total order"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/anti-scraper-techniques/",
    "url": "https://brandonrozek.com/blog/anti-scraper-techniques/",
    "title": "Dealing with Web Scrapers",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eNowadays it seems like every tech company is eager to scrape the web. Unfortunately, it seems like\n\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e the majority of traffic that comes to this small site are scrapers. While my static website is able to handle the load, the same cannot be said about everyone.\u003c/p\u003e\n\u003cp\u003eOverall, the techinques I\u0026rsquo;ve seen website owners use aim to make scraping more difficult. Though it\u0026rsquo;s a balance. The harder we make it for bots to access a website, the more we turn away regular humans as well. Here\u0026rsquo;s a short and non-exhaustive list of techinques:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eUser Agent Filtering\u003c/li\u003e\n\u003cli\u003eCAPTCHA solving\u003c/li\u003e\n\u003cli\u003eRate Limiting\u003c/li\u003e\n\u003cli\u003eProof of work\u003c/li\u003e\n\u003cli\u003eIdentification\u003c/li\u003e\n\u003cli\u003ePaywall\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 id=\"user-agent-filtering\"\u003eUser Agent Filtering\u003c/h3\u003e\n\u003cp\u003eWhen a person/bot requests a page from a website, the HTTP header of the request has a field called \u003ccode\u003eUser-Agent\u003c/code\u003e.  This is to denote the type of client that the requester is using. For example, when I visited a website just now, I sent the user agent \u003ccode\u003eMozilla/5.0 (X11; Linux x86_64; rv:139.0) Gecko/20100101 Firefox/139.0\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFiltering based on this string is the easiest technique to employ and also has a low chance of impacting regular humans visiting the website. \u003ca href=\"https://www.rfc-editor.org/rfc/rfc9309.html\"\u003eRFC 9309 Robots Exclusion Protocol\u003c/a\u003e, more commonly known as \u003ccode\u003erobots.txt\u003c/code\u003e, is the most common way of implementing this technique.\u003c/p\u003e\n\u003cp\u003eHow it works is that you create a file named \u003ccode\u003erobots.txt\u003c/code\u003e at the root directory of your website and write a set of rules that different robots \u003cem\u003eshould\u003c/em\u003e follow. Here\u0026rsquo;s an example from \u003ca href=\"https://developers.google.com/search/docs/crawling-indexing/robots/create-robots-txt\"\u003eGoogle\u0026rsquo;s search documentation\u003c/a\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-txt\" data-lang=\"txt\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eUser-agent: Googlebot\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDisallow: /nogooglebot/\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eUser-agent: *\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAllow: /\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSitemap: https://www.example.com/sitemap.xml\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003e*\u003c/code\u003e here is the Klenne star which means that it can match any string. Before the bot requests a page, the idea is that they first request this \u003ccode\u003erobots.txt\u003c/code\u003e file, find the rules that match their user agent, and follow it\u0026rsquo;s instructions.\u003c/p\u003e\n\u003cp\u003eAs you might imagine, not everyone writes scrapers that follow these rules. This depends on how well-written the bot was and how considerate the developer is. An alternative to this approach is to block the request at the web server. For example, here\u0026rsquo;s how you would do that using \u003ccode\u003enginx\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e(\u003c/span\u003e$http_user_agent = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Googlebot\u0026#34;)\u003c/span\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e403\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis returns an empty response with the HTTP code \u003ccode\u003e403 Forbidden\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThe downside to this approach is that it\u0026rsquo;s easy to pretend that you have a different user agent. For example on my machine, the user agent set by \u003ccode\u003ecurl\u003c/code\u003e is \u003ccode\u003ecurl/8.9.1\u003c/code\u003e. However, I can use the same user agent as my browser by adding a flag:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl --user-agent \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Mozilla/5.0 (X11; Linux x86_64; rv:139.0) Gecko/20100101 Firefox/139.0\u0026#34;\u003c/span\u003e https://brandonrozek.com\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"captcha-solving\"\u003eCAPTCHA Solving\u003c/h3\u003e\n\u003cp\u003eThe Completely Automated Public Turing test to tell Computers and Humans Apart (CAPTCHA) is a challenge-response approach to dealing with bots. The idea is that the webserver would present some sort of challenge that is supposedly hard for computers to solve but easy for humans. The human responds to the challenge and then is granted access to the website.\u003c/p\u003e\n\u003cp\u003eIn the paper \u0026ldquo;Recent advances of Captcha security analysis: a short literature review\u0026rdquo; by Nghia Trong Dinh and Vinh Truong Hoang, they show that for the majority of CAPTCHA systems, bots are successful at solving them over 50% of the time. Specifically, the best bots are able to solve Google\u0026rsquo;s image-based CAPTCHAs with 70.78% accuracy.\u003c/p\u003e\n\u003cp\u003eUnfortunately\u003csup id=\"fnref:2\"\u003e\u003ca href=\"#fn:2\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e2\u003c/a\u003e\u003c/sup\u003e the success rate of bots are bound to improve over time. Additionally, CAPTCHA systems are annoying to humans. For example, when I use a VPN, I don\u0026rsquo;t bother with Google search since I don\u0026rsquo;t want to select pictures of stairs, fire hydrants, or crosswalks 10 times before being granted a search query.\u003c/p\u003e\n\u003ch3 id=\"rate-limiting\"\u003eRate Limiting\u003c/h3\u003e\n\u003cp\u003eComputers are inherently faster than us. In the paper \u0026ldquo;How many words do we read per minute? A review and meta-analysis of reading rate\u0026rdquo; by Marc Brysbaert, he writes that the average human adult reads 238 words per minute of non-fiction silently. Thus, it would take a human on average almost 11 hours to read all my prior blog posts (assuming they don\u0026rsquo;t get tired or distracted). Meanwhile a bot can scrape this site in under a minute.\u003c/p\u003e\n\u003cp\u003eFrom this insight, one technique is to limit the number of requests that an IP address can make at any given time. This is formally known as \u003cem\u003erate limiting\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eIt sounds simple in concept but can be difficult to implement without impacting user experience. How many requests is a human reasonably allowed to make in a minute? Human traffic is typically bursty, where a page load can request many different files (CSS, JS, media) in a short period of time.  How quick can I expect someone to reasonably click around my website? If this isn\u0026rsquo;t dialed in properly, then rate limiting can cause frustration with your visitors.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;m also unsure how successful this is against the LLM web scrapers. Nowadays there are bot farms where they each have their own IP address. It\u0026rsquo;s difficult to determine whether a request is from a human visitor or part of a larger bot collection network.\u003c/p\u003e\n\u003ch3 id=\"proof-of-work\"\u003eProof of work\u003c/h3\u003e\n\u003cp\u003eWe talked about how CAPTCHAs are difficult for computers but easy for humans. Proof of work is difficult for both computers and humans. This helps reduce the number of scrapers by making it \u003cem\u003ecostly\u003c/em\u003e to request resources from the website. By making the web browser solve some proof of work challenge (usually involving hash functions), the request consumes additional CPU cycles and takes additional time.\u003c/p\u003e\n\u003cp\u003eSimilar to rate limiting, how \u003cem\u003edifficult\u003c/em\u003e you make the problem has a direct impact on user experience. The more difficult, the longer it\u0026rsquo;ll take for the web browser to solve it. This will deter more bots, but after a few seconds will also deter human visitors. \u003ca href=\"https://web.archive.org/web/20250121155519/https://www.thinkwithgoogle.com/marketing-strategies/app-and-mobile/page-load-time-statistics/\"\u003eAccording to a study performed by Google and SOASTA Research in 2017\u003c/a\u003e, if a user has to wait 3 seconds instead of 1 second, then the probability that they \u003cem\u003ebounce\u003c/em\u003e (leave the page) increases by 32%.\u003c/p\u003e\n\u003cp\u003eRecently, open-source projects \u003ca href=\"https://anubis.techaro.lol/\"\u003eAnubis\u003c/a\u003e and \u003ca href=\"https://git.gammaspectra.live/git/go-away\"\u003ego-away\u003c/a\u003e gained popularity for making it easy to implement this technique. It\u0026rsquo;s popular for git forges like \u003ca href=\"https://git.sr.ht/\"\u003esourcehut\u0026rsquo;s\u003c/a\u003e as scraping those incurs a lot of CPU cycles in traversing git repositories.\u003c/p\u003e\n\u003ch3 id=\"identification\"\u003eIdentification\u003c/h3\u003e\n\u003cp\u003eAnother tactic is to ask the requester to provide some information that a human would likely have but a bot less so. Examples include email addresses, phone number, government ID, etc. Of course, a bot can supply false information, but as with the other techinques this adds an additional barrier. Watch out for the \u003ca href=\"https://gregoryhammond.ca/blog/never-to-connect-phone-numbers-a-project/\"\u003efake phone numbers\u003c/a\u003e.\u003c/p\u003e\n\u003ch3 id=\"paywall\"\u003ePaywall\u003c/h3\u003e\n\u003cp\u003eLastly, you can require users to pay to see the contents of your website. This is popular with news organizations where they ask you to pay for a subscription in order to see content. This ties in well with the previous tactic, because if the user pays for a subscription, then you likely have a lot of identifying information about that user.\u003c/p\u003e\n\u003cp\u003eAnother interesting idea that I haven\u0026rsquo;t seen widely implemented is requiring some amount of money per interaction. This can be in the form of the \u003ca href=\"https://webmonetization.org/\"\u003eWeb Monetization API\u003c/a\u003e or via cryptocurrency like Bitcoin on the \u003ca href=\"https://lightning.network/\"\u003eLightning network\u003c/a\u003e. \u003ca href=\"https://stacker.news/\"\u003eStacker news\u003c/a\u003e is an example of a Reddit-like platform where users need to pay a small fee in order to upvote a post. The idea is to make it cheap for a human to do on a small scale (like 1 cent per up-vote), but expensive for a bot to do at scale.\u003c/p\u003e\n\u003ch3 id=\"conclusion\"\u003eConclusion\u003c/h3\u003e\n\u003cp\u003eWe\u0026rsquo;re in a special time period where everyone is fighting to become the top AI company. Long term, I feel that the scraper activity will die down. Similar to how there weren\u0026rsquo;t as many web search scrapers out there.\u003c/p\u003e\n\u003cp\u003eIn the meantime, these are multiple techniques to consider if your website is suffering under heavy load. As for myself, I don\u0026rsquo;t currently implement any of these as my website is mostly static and I haven\u0026rsquo;t noticed my servers being overloaded.\u003c/p\u003e\n\u003cp\u003eHowever if you do, I urge you to exercise some caution. For the most part, we share on the web for information to flow freely, and if we\u0026rsquo;re not careful we may drive people away.\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eAt least I don\u0026rsquo;t think a human using Chrome would try to visit my homepage every minute.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:2\"\u003e\n\u003cp\u003eOr fortunately, if we want to get closer to AGI\u0026#160;\u003ca href=\"#fnref:2\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2025.07.02",
    "tags": ["web scraping","CAPTCHA","rate limiting","robots.txt","proof of work"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/exploring-via-public-transit/",
    "url": "https://brandonrozek.com/blog/exploring-via-public-transit/",
    "title": "Exploring via Public Transit",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLast weekend, on my way back north from visiting downtown, I took a break near the Crestview lightrail station. There, I had a wonderful meal at the \u003ca href=\"https://kurasushi.com/locations/austin-tx-airport-blvd/\"\u003eKura revolving sushi bar\u003c/a\u003e. Afterwards, I walked around the \u003ca href=\"https://usa.kinokuniya.com/stores-kinokuniya-austin\"\u003eKinokuniya Bookstore\u003c/a\u003e. Honestly, this spot is not something I would\u0026rsquo;ve naturally discovered on my own.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/202506071440.png\" alt=\"Image of Sushi Conveyor Belt\"\u003e\u003c/p\u003e\n\u003cp\u003eUsing public transportation is a great way to explore the neighborhoods around you. Busses often don\u0026rsquo;t take highways, and instead will take you through areas that you would\u0026rsquo;ve otherwise skipped. You can find many great restaurants to eat in the Bay Area right next to the Mountain View Caltrain station. A few years ago when Clare and I visited Portland, Maine, we got to explore the thousand islands by a \u003ca href=\"https://www.cascobaylines.com/maine-boat-tours/specialty-cruises/mailboat/\"\u003email boat\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eEven if you have a car, it\u0026rsquo;s worth taking a look at the transit maps to see if there are any hidden gems.\u003c/p\u003e\n",
    "date_published": "2025.06.15",
    "tags": ["public transport"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/embrace-the-heat/",
    "url": "https://brandonrozek.com/blog/embrace-the-heat/",
    "title": "Embrace the Heat",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIt\u0026rsquo;s summer and I\u0026rsquo;m currently in Austin, Texas, where the temperature ranges between 85 F when we had a recent thunderstorm to 100 F when there are no clouds in the sky.\u003c/p\u003e\n\u003cp\u003eI don\u0026rsquo;t currently have a car so I rely on public transportation and walking to get around. As such, I\u0026rsquo;m consistently exposed to the heat. On the weekends, there is a strong temptation to hunker down and enjoy my air-conditioned apartment.  However, the part of me that loves going out and exploring wins in the end.\u003c/p\u003e\n\u003cp\u003eAs with any outdoor adventure, it\u0026rsquo;s always important to be prepared. I don\u0026rsquo;t leave my apartment without the following three items: bucket hat, sunglasses, and a water pack. Out of these, I would say the most important is drinking lots of water. It\u0026rsquo;s the best way to \u003ca href=\"https://www.cdc.gov/healthy-weight-growth/water-healthy-drinks/index.html\"\u003eregulate body temperature\u003c/a\u003e after all. Also, it\u0026rsquo;s better to start your day early, before the temperatures climb. Even after the sun goes down, it takes a while for the temperature to drop.\u003c/p\u003e\n\u003cp\u003eWhile browsing Reddit, I came across the phrase \u0026ldquo;Embrace the Heat.\u0026rdquo; In my adventures, I see plenty of people eating outdoors, walking on trails, and paddle boarding on the Colorado river. It says a lot about human resiliency that we make the most out of any situation.\u003c/p\u003e\n\u003cp\u003eYesterday, I rented a bicycle from Cap Metro and rode along Lady Bird lake for two hours. Before that I\u0026rsquo;ve been graced by the presence of the \u003ca href=\"https://peasepark.org/pease-park-troll\"\u003eAustin Troll Malin\u003c/a\u003e, and I\u0026rsquo;m looking forward to kayaking at Barton Springs.\u003c/p\u003e\n\u003cp\u003eIf you happen to visit Austin and want an indoor activity, I recommend visiting the \u003ca href=\"https://tspb.texas.gov/plan/tours/tours.html\"\u003estate capitol\u003c/a\u003e. They allow visitors to sit in the gallerie and observe when the state house and senate is in session which is pretty cool to witness.\u003c/p\u003e\n\u003cp\u003eOtherwise, embrace the heat. Wear light clothes and come prepared to sweat.\u003c/p\u003e\n",
    "date_published": "2025.06.08",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/verifying-proofs-type-checkers/",
    "url": "https://brandonrozek.com/blog/verifying-proofs-type-checkers/",
    "title": "Verifying Proofs with Type Checkers",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe \u003ca href=\"https://en.wikipedia.org/wiki/Curry%E2%80%93Howard_correspondence\"\u003eCurry-Howard Correspondance\u003c/a\u003e establishes a direct connection between computer programs and mathematical proofs.\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003ePrograms\u003c/th\u003e\n          \u003cth\u003eProofs\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eType\u003c/td\u003e\n          \u003ctd\u003eFormula\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eTerm\u003c/td\u003e\n          \u003ctd\u003eProof\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eType has an element\u003c/td\u003e\n          \u003ctd\u003eFormula is provable\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eType does not have an element\u003c/td\u003e\n          \u003ctd\u003eFormula is not provable\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eWhen we think about traditional type systems, however, most are uncapable of expressing interesting formula. This ultimately limits the formulas we can prove.\u003c/p\u003e\n\u003cp\u003eFor example, consider the simply typed lambda calculus. In that system we have base types like \u003ccode\u003eint\u003c/code\u003e and \u003ccode\u003ebool\u003c/code\u003e, as well as function types $t1 \\rightarrow t2$ which take a type and return a type.\u003c/p\u003e\n\u003cp\u003eFrom that system, we can then create a proof of an integer by showing the term \u003ccode\u003e0\u003c/code\u003e.  The following shows how to use the Lean 4 type checker to verify that \u003ccode\u003e0\u003c/code\u003e is an integer:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003e#check (0: Int)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis, however, isn\u0026rsquo;t a very interesting proof or formula. What would be more interesting is if we can use a type checker to verify the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003e#check (by exists [] : ∃ (x : List Int), x.length = 0)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eSadly, we can\u0026rsquo;t verify this with the simply typed lambda calculus. This does not mean that we cannot design a type system that can.\u003c/p\u003e\n\u003cp\u003eInteractive theorem provers (ITPs) or \u003ca href=\"https://en.wikipedia.org/wiki/Proof_assistant\"\u003eproof assistants\u003c/a\u003e help fill this void. Equipped with sophisticated type systems, they allow us to prove a formula by writing a program of that type. Some of the most popular ones include \u003ca href=\"https://lean-lang.org/\"\u003eLean\u003c/a\u003e, \u003ca href=\"https://rocq-prover.org/\"\u003eRocq\u003c/a\u003e, \u003ca href=\"https://isabelle.in.tum.de/\"\u003eIsabelle\u003c/a\u003e, \u003ca href=\"https://wiki.portal.chalmers.se/agda/pmwiki.php\"\u003eAgda\u003c/a\u003e, and \u003ca href=\"https://fstar-lang.org/\"\u003eF*\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThe underlying type system of each proof assistant is different. However, there are many similarities. In general, most aim to be at least as powerful as the calculus of constructions developed by Thierry Coquand.\u003c/p\u003e\n\u003cp\u003eTo get an idea of how expressive these type systems are, we can analyze the \u003ca href=\"https://en.wikipedia.org/wiki/Lambda_cube\"\u003eLambda cube\u003c/a\u003e. Developed by Henk Barendregt,  this characterizes the space between the simply typed lambda calculus and the calculus of constructions.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/Lambda_Cube_img.svg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://commons.wikimedia.org/wiki/File:Lambda_Cube_img.svg\"\u003eImage courtesy of Tellofou on Wikipedia\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eEach dimension corresponds to an extension of the simply typed lambda calculus:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eX Axis: Dependent types (Types that depend on terms)\u003c/li\u003e\n\u003cli\u003eY Axis: Polymorphic types (Terms that depend on types)\u003c/li\u003e\n\u003cli\u003eZ Axis: Type Operators (Types that depend on types)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eDependent Type\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eA dependent type is a type that depends on some term. We can use this to, for example, write functions that return entirely different base types depending on the outcome of some conditional.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s an example function you can write in Lean 4:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003edef isZeroOrMore (n : Nat) : (if n == 0 then Bool else Nat) :=\n  if H: n == 0 then by\n    rw [if_pos (by exact H)]\n    exact true\n  else by\n    rw [if_neg (by exact H)]\n    exact n\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe function \u003ccode\u003eisZeroOrMore\u003c/code\u003e will return \u003ccode\u003etrue\u003c/code\u003e if the parameter \u003ccode\u003en\u003c/code\u003e is equal to zero, otherwise it\u0026rsquo;ll return the natural number parameter itself.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eType Operators\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eA type operator takes a type and produces a type. For example, we can create a triple type that represents the product of three base types.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003estructure Triple {A B C : Type} where\n  a: A\n  b: B\n  c: C\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eLet\u0026rsquo;s say that we want to enforce that all three elements of the triple are of type \u003ccode\u003eInt\u003c/code\u003e. Then we can create a new type \u003ccode\u003eIntTriple\u003c/code\u003e as follows:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-Lean\" data-lang=\"Lean\"\u003edef IntTriple := @Triple Int Int Int\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFrom here, we can use \u003ccode\u003eIntTriple\u003c/code\u003e when type checking values.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#check (Triple.mk 1 2 3 : IntTriple)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003eType Polymorphism\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eType polymorphism allows us to write one function which covers many different types. There are two common places that we see this often.\u003c/p\u003e\n\u003cp\u003eIdentity function:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003edef iden {α : Type} (x : α) : α := x\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eHere we\u0026rsquo;re saying that the parameter a can be of any arbitrary type α, and the function \u003ccode\u003eiden\u003c/code\u003e will then just return the parameter itself.\u003c/p\u003e\n\u003cp\u003eWe also see this often when defining operations over lists\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003edef map {α β : Type} (f: α → β) (as: List α): List β :=\n  match as with\n  | [] =\u0026gt; []\n  | (head::tail) =\u0026gt; f head :: map f tail\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe calculus of constructions support all of these three extensions.  We\u0026rsquo;ve introduced enough here to write a more interesting proof.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003etheorem map_iden_idempotent {α : Type} (as: List α) : (map iden as = as) := by\n  unfold iden\n  induction as\n  case nil =\u0026gt;\n    unfold map\n    rfl\n  case cons h t IH =\u0026gt;\n    unfold map\n    rw [IH]\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe\u0026rsquo;ve invoked the Lean type checker to verify that for a list of any arbitrary type, when you map over it with the identity function you get back the list itself.\u003c/p\u003e\n\u003cp\u003eYou might ask, why don\u0026rsquo;t all programming languages support this? By adding expressivity to our type system, we sacrifice computational efficiency. For most standard programming tasks, we want our type checker to be as fast as possible. However when we require stronger guarentees of correctness about our code, proof assistants are a great tool for the job. Many of them even generate code for mainstream programming languages.\u003c/p\u003e\n\u003cp\u003eThat\u0026rsquo;s all for today. I\u0026rsquo;d like to thank James Oswald for the inspiration to write this post and some of its examples. This was adapted from a presentation we gave last month to our weekly seminar series.\u003c/p\u003e\n",
    "date_published": "2025.05.27",
    "tags": ["Formal methods","proof assistant","Curry-Howard correspondence","lambda cube","calculus of constructions","dependent type","type theory"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/program-safety-type-theory/",
    "url": "https://brandonrozek.com/blog/program-safety-type-theory/",
    "title": "Is this program safe? Lessons from Type Theory",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cem\u003eThis blog post is inspired from a lecture that Dr. Ana Milanova gave in her Principles of Program Analysis course.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eConsider an arbitrary computer program. When we execute it, we expect that it will produce some result and not get \u003cem\u003estuck\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eFor example,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eid \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003elambda\u003c/span\u003e x: x\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eid \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn the code above, we create an identity function \u003ccode\u003eid\u003c/code\u003e which takes an argument \u003ccode\u003ex\u003c/code\u003e and returns that same argument. Therefore, in the second line when we call the function with the value \u003ccode\u003e5\u003c/code\u003e, we receive the value \u003ccode\u003e5\u003c/code\u003e as a result.\u003c/p\u003e\n\u003cp\u003eNow what about the following?\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e id\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis program does not make any sense since the value \u003ccode\u003e5\u003c/code\u003e is not a function and hence does not take anything as an argument. Ideally, we want to catch errors like these before we run our code. In fact, we can by relying on a \u003cem\u003etype checker\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eThrough this, we only execute programs that have a valid \u003cem\u003etype\u003c/em\u003e. If not, then we say that the program is invalid.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eid 5\u003c/code\u003e is of type \u003ccode\u003eint\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e5 id\u003c/code\u003e does not have a valid type\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWe won\u0026rsquo;t delve into the formal theory of type systems, however, know that we can design a type system to be as complicated as we\u0026rsquo;d like. We can even go as far as verifying whether the result equals an expected value at the end of program execution. However, the \u003ca href=\"https://en.wikipedia.org/wiki/Halting_problem\"\u003eHalting problem\u003c/a\u003e from theoretical computer science tells us that we cannot write an algorithm that\u0026rsquo;s guaranteed to eventually end and tell us whether an inputted program will return a specific result.\u003c/p\u003e\n\u003cp\u003eDue to this, we need to make some compromises in type checkers. We really want it so that if a program passes the type checker, then it will never get stuck during execution by encountering something like \u003ccode\u003e5 id\u003c/code\u003e. Therefore, instead we compromise by failing programs that will actually never get stuck.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eDesigning a type system is a trade off between runtime and how many valid programs we are able to call type safe.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eIn other words, there will be programs that are valid and produce a result, however our type system will not accept it.\u003c/p\u003e\n\n\n\u003cimg alt=\"Venn Diagram showing that type accepted programs are smaller and within the space of valid programs\" src=\"/files/images/blog/type-safety-venn.svg\" width=\"500px\"/\u003e\n\n\n\u003cp\u003eFor example, let us consider one of the simplest type systems. That is the simply typed lambda calculus. We won\u0026rsquo;t go into the formal details of its presentation, but instead highlight how it won\u0026rsquo;t allow all valid programs through an example.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eid \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003elambda\u003c/span\u003e x : x\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (id true) then (id \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e) \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e (id \u003cspan style=\"color:#ae81ff\"\u003e6\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eConsider again our identity function but this time it is used within an if-then-else (ite) expression. When we run this code, it will return the value \u003ccode\u003e5\u003c/code\u003e which is of type \u003ccode\u003eint\u003c/code\u003e. However, what does the type checker think?\u003c/p\u003e\n\u003cp\u003eIn the simply typed lambda calculus, the type of an expression may either be a base type like an \u003ccode\u003eint\u003c/code\u003e or \u003ccode\u003e bool\u003c/code\u003e, or it can be a function type which takes a type and returns a type (i.e,\u003ccode\u003etype\u003c/code\u003e $\\rightarrow$ \u003ccode\u003etype\u003c/code\u003e).\u003c/p\u003e\n\u003cp\u003eFrom the definition of \u003ccode\u003eid\u003c/code\u003e, we cannot determine what type it has. Instead we know that for some type variable $t$, it is $t \\rightarrow  t$.\u003c/p\u003e\n\u003cp\u003eThen, we take a look at the conditional part of the ite expression. We derive that \u003ccode\u003eid\u003c/code\u003e must have type \u003ccode\u003ebool\u003c/code\u003e $\\rightarrow$ \u003ccode\u003ebool\u003c/code\u003e. However, the other two parts of the ite expression requires that \u003ccode\u003eid\u003c/code\u003e takes and returns an \u003ccode\u003eint\u003c/code\u003e!\u003c/p\u003e\n\u003cp\u003eSince \u003ccode\u003ebool\u003c/code\u003e and \u003ccode\u003eint\u003c/code\u003e are two distinct types, a type checker based on the simply typed lambda calculus will not accept this program.\u003c/p\u003e\n\u003cp\u003eIn order for our type checker to accept this program, the underlying type system must support polymorphic types (sometimes called polytypes). An example system that does is System F.\u003c/p\u003e\n\u003cp\u003eThe biggest difference during type inference, is that instead of determining that \u003ccode\u003eid\u003c/code\u003e has type $t \\rightarrow t$ for some type variable $t$. It has type $\\forall t, t \\rightarrow t$. Thus, allowing for both an identity function that takes a \u003ccode\u003ebool\u003c/code\u003e and returns a \u003ccode\u003ebool\u003c/code\u003e, and for an identity function to take an \u003ccode\u003eint\u003c/code\u003e and return an \u003ccode\u003eint\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eEven with polytypes, our type checker will still not accept all valid programs! A great overview on the different dimensions of type systems is on the Wikipedia page for \u003ca href=\"https://en.wikipedia.org/wiki/Lambda_cube\"\u003eLambda Cube\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eIn our examples above, we had our type checker also perform inference to identify what the type of the expressions are. However, we can use more efficient algorithms if we forced the writer of the program to annotate the types instead. This is another trade off to think about when designing a programming language.\u003c/p\u003e\n\n\n\u003cimg alt=\"Venn Diagram showing that simply type accepted programs are smaller and within system f accepted programs which are within space of valid programs\" src=\"/files/images/blog/type-safety-simply-systemf-venn.svg\" width=\"500px\"/\u003e\n\n\n\u003cp\u003eOr, we can throw all caution to the wind and have our program crash and throw exceptions instead.\u003c/p\u003e\n\u003cp\u003eExample in Python:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003elambda\u003c/span\u003e x: x)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eTraceback (most recent call last):\n  File \u0026#34;\u0026lt;stdin\u0026gt;\u0026#34;, line 1, in \u0026lt;module\u0026gt;\nTypeError: \u0026#39;int\u0026#39; object is not callable\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2025.05.10",
    "tags": ["type theory","type inference"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/filesystem-as-persistent-kvs-python/",
    "url": "https://brandonrozek.com/blog/filesystem-as-persistent-kvs-python/",
    "title": "Filesystem as a persistent key-value store in Python",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe first level within the \u003ca href=\"https://beza1e1.tuxen.de/config_levels.html\"\u003efive levels of configuration languages\u003c/a\u003e is a string in a file. The example that Andreas gives in his blog post is enabling/disabling energy aware scheduling in the kernel.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Read the current value\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecat /proc/sys/kernel/sched_energy_aware\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis approach does not require using any custom serializers or deserializers. Instead, the user or program only needs to navigate and read/write the filesystem.\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003cp\u003eHowever, having to write filesystem code is tedious. What we want is a dictionary that we can write and read from that handles all of the filesystem procedures for us.\u003c/p\u003e\n\u003cp\u003eIn this post, we\u0026rsquo;ll go over how to achieve this in Python. By the end we\u0026rsquo;ll be able to do the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Create key-value store\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir /tmp/kvs\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Add value \u0026#34;Brandon\u0026#34; to key \u0026#34;name\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e | tee /tmp/kvs/name\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Set /tmp/kvs as the root of our key-value store\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ekvs \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e FSDict(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/tmp/kvs\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Read the key \u0026#39;name\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Name:\u0026#34;\u003c/span\u003e, kvs[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;name\u0026#39;\u003c/span\u003e])\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Name: Brandon\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"creating-the-library\"\u003eCreating the library\u003c/h2\u003e\n\u003cp\u003eTo emulate reading and writing from a dictionary, we will create a custom class and implement the \u003ccode\u003e__setitem__\u003c/code\u003e and \u003ccode\u003e__getitem__\u003c/code\u003e methods.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eFSDict\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __init__(self, base_directory: Optional[str] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebase_directory: Optional[str] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e base_directory \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eset_base_directory(base_directory)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eset_base_directory\u003c/span\u003e(self, base_directory: str):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eassert\u003c/span\u003e isinstance(base_directory, str)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eisfile(base_directory):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eException\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;base_directory is an existing file not a folder.\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebase_directory \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e base_directory\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eexists(base_directory):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emakedirs(base_directory)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e# ...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFrom here, we can declare a folder as the root of our key-value store and if it does not exist then the code will create it for us.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ekvs \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e FSDict(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/tmp/kvs\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhen we write \u003ccode\u003ekvs['name']\u003c/code\u003e, we want it to read from the file or folder \u003ccode\u003e/tmp/kvs/name\u003c/code\u003e. Our next method then will create the path.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eget_path\u003c/span\u003e(self, key: str) \u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003e str:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eassert\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebase_directory \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e isinstance(key, str):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eValueError\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;key must be of type str\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ejoin(self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebase_directory, key)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eJust like in our initial kernel example, we want a way to have nested folders. Therefore, in this API when we read and write from the KVS, if the value is a \u003ccode\u003estr\u003c/code\u003e then we\u0026rsquo;re writing it\u0026rsquo;s contents to a file, otherwise if it is a \u003ccode\u003eFSDict\u003c/code\u003e then we\u0026rsquo;ll create the folder if it does not already exist.\u003c/p\u003e\n\u003cp\u003eTo write to our key-value store:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __setitem__(self, key: str, value: Union[str, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;FSDict\u0026#34;\u003c/span\u003e]):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eassert\u003c/span\u003e isinstance(value, (str, FSDict)), \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Value must either be of type str or FSDict\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  path \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eget_path(key)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e# If we\u0026#39;re writing a FSDict, then create\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e# a folder if it doesn\u0026#39;t already exist.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e isinstance(value, FSDict):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    value\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eset_base_directory(path)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e# Otherwise, assume we\u0026#39;re writing a\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e# string to a file\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e open(path, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;w\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e f:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    f\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ewrite(value)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo read from our key-value store:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __getitem__(self, key: str) \u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003e Union[str, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;FSDict\u0026#34;\u003c/span\u003e]:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  path \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eget_path(key)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eexists(path):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eKeyError\u003c/span\u003e(key)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e# Return a FSDict if it\u0026#39;s a folder\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eisdir(path):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e FSDict(path)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e# Otherwise, assume we\u0026#39;re reading a file\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e open(path, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;r\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e f:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e f\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eread()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWith that, we have the minimum viable product for reading and writing to our dictionary-like class!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ekvs \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e FSDict(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/tmp/kvs\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ekvs[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;name\u0026#39;\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Brandon\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(kvs[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;name\u0026#39;\u003c/span\u003e])\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Prints \u0026#39;Brandon\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe base dictionary class has other methods that make our lives easier. Let\u0026rsquo;s implement a few more.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __delitem__(self, key: str):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  path \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eget_path(key)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eexists(path):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eKeyError\u003c/span\u003e(key)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e# If the key is a folder, recursively\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e# remove all contents of that folder\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eisdir(path):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    shutil\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ermtree(path)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e# Otherwise, remove the file\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eremove(path)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __contains__(self, key: str) \u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003e bool:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  path \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eget_path(key)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eexists(path)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e(self) \u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003e List[str]:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eassert\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebase_directory \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elistdir(self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebase_directory)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __repr__(self) \u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003e str:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ef\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;FSDict(base_directory=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{\u003c/span\u003eself\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebase_directory\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e)\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eExtending our last example, this gives us:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Show all keys\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(kvs\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ekeys())\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Result: [\u0026#39;name\u0026#39;]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check if a key is in the key-value store\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;name\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e kvs)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Result: True\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Delete a key-value by it\u0026#39;s key\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edel\u003c/span\u003e kvs[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;name\u0026#39;\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check if it still exists in our key-value store\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;name\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e kvs)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Result: False\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eThe class \u003ccode\u003eFSDict\u003c/code\u003e provides a simple way in Python to use the filesystem as a persistent key-value store. Unlike storing everything in a single file or database, we do not need special serializers or deserializers to access to data. Outside the program we can use \u003ccode\u003eecho\u003c/code\u003e and \u003ccode\u003ecat\u003c/code\u003e in our terminals to interact with the key-value store.\u003c/p\u003e\n\u003cp\u003eThe full code is as follows:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e typing \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e List, Optional, Union\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e os\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e shutil\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eFSDict\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __init__(self, base_directory: Optional[str] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebase_directory: Optional[str] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e base_directory \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eset_base_directory(base_directory)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eset_base_directory\u003c/span\u003e(self, base_directory: str):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eassert\u003c/span\u003e isinstance(base_directory, str)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eisfile(base_directory):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eException\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;base_directory is an existing file not a folder.\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebase_directory \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e base_directory\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eexists(base_directory):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emakedirs(base_directory)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eget_path\u003c/span\u003e(self, key: str) \u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003e str:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eassert\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebase_directory \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e isinstance(key, str):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eValueError\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Key must be of type str\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ejoin(self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebase_directory, key)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __setitem__(self, key: str, value: Union[str, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;FSDict\u0026#34;\u003c/span\u003e]):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eassert\u003c/span\u003e isinstance(value, (str, FSDict)), \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Value must either be of type str of FSDict\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        path \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eget_path(key)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e isinstance(value, FSDict):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            value\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eset_base_directory(path)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# Assuming value is a str\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e open(path, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;w\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e f:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            f\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ewrite(value)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __getitem__(self, key: str) \u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003e Union[str, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;FSDict\u0026#34;\u003c/span\u003e]:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        path \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eget_path(key)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eexists(path):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eKeyError\u003c/span\u003e(key)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eisdir(path):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e FSDict(path)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# Assume it\u0026#39;s a file\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e open(path, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;r\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e f:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e f\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eread()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __delitem__(self, key: str):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        path \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eget_path(key)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eexists(path):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eKeyError\u003c/span\u003e(key)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eisdir(path):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            shutil\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ermtree(path)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# Assume it\u0026#39;s a file\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eremove(path)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __contains__(self, key: str) \u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003e bool:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        path \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eget_path(key)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eexists(path)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e(self) \u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003e List[str]:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eassert\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebase_directory \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elistdir(self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebase_directory)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __repr__(self) \u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003e str:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ef\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;FSDict(base_directory=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{\u003c/span\u003eself\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebase_directory\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e)\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eYou likely want to make sure that your program is sufficiently isolated. Either by running it within it\u0026rsquo;s own container or setting up custom permissions on the files.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2025.04.27",
    "tags": ["key-value database","file system","Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/quick-lean-if-then-else-in-hypothesis/",
    "url": "https://brandonrozek.com/blog/quick-lean-if-then-else-in-hypothesis/",
    "title": "Quick Lean: if-then-else statement in hypothesis",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen verifying proofs about code, I often end up with a hypothesis that has an if statement in it. Depending on how long it\u0026rsquo;s been since I last used Lean, I forget how to deal with it. This is a quick post to remind me how.\u003c/p\u003e\n\u003cp\u003eExample:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample (b: Bool) (n: Nat) (H1: if b then (n = 5) else (n = 3)) : n \u0026gt; 0 := by sorry\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe want to show that for an arbitrary boolean and natural number \u003ccode\u003en\u003c/code\u003e, that \u003ccode\u003en\u003c/code\u003e will always be greater than zero.\u003c/p\u003e\n\u003cp\u003eNote that the condition of an arbitrary if statement can take a proposition instead of a boolean. However for this to work the proposition must be \u003cem\u003edecidable\u003c/em\u003e. Checking the condition of a boolean is decidable, so we\u0026rsquo;ll use that to simplify our example.\u003c/p\u003e\n\u003cp\u003eFrom here, we use \u003ccode\u003eby_cases (b = true)\u003c/code\u003e to give us two new subgoals. One where it\u0026rsquo;s true and one where it\u0026rsquo;s false.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eby_cases H2 : (b = true)\ncase pos =\u0026gt;\n  sorry\ncase neg =\u0026gt;\n  sorry\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFirst let\u0026rsquo;s consider the positive case. We can simplify \u003ccode\u003eH1\u003c/code\u003e to \u003ccode\u003en = 5\u003c/code\u003e by using \u003ccode\u003eH2\u003c/code\u003e which states that \u003ccode\u003eb\u003c/code\u003e is true.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003ereplace H1 : n = 5 := by\n  rwa [if_pos (by exact H2)] at H1\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFor this example, you don\u0026rsquo;t need the parenthesis saying \u003ccode\u003e(by exact H2)\u003c/code\u003e. However, depending on your setup the proof that \u003ccode\u003eb\u003c/code\u003e is true might be too difficult for the rewrite system to infer. In those cases, you are required to specify the proof for it to work.\u003c/p\u003e\n\u003cp\u003eThen, we can substitute \u003ccode\u003en\u003c/code\u003e to have our subgoal as \u003ccode\u003e5 \u0026gt; 0\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003esubst n\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis is the same as showing that \u003ccode\u003e0 \u0026lt; 5\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003esuffices 0 \u0026lt; 5 by exact gt_iff_lt.mpr this\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFrom here we can apply one of the builtin theorems\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexact Nat.zero_lt_succ 4\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFor the negative case, we will follow a similar pattern except instead of invoking \u003ccode\u003eif_pos\u003c/code\u003e to eliminate the if-statement, we will invoke \u003ccode\u003eif_neg\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThus, the complete proof for this is\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample (b : Bool) (n : Nat) (H1: if b then (n = 5) else (n = 3)) : n \u0026gt; 0 := by\n  by_cases H2 : (b = true)\n  case pos =\u0026gt;\n    replace H1 : n = 5 := by\n      rwa [if_pos (by exact H2)] at H1\n    subst n\n    suffices 0 \u0026lt; 5 by exact gt_iff_lt.mpr this\n    exact Nat.zero_lt_succ 4\n  case neg =\u0026gt;\n    replace H1 : n = 3 := by\n      rwa [if_neg (by exact H2)] at H1\n    subst n\n    suffices 0 \u0026lt; 3 by exact gt_iff_lt.mpr this\n    exact Nat.zero_lt_succ 2\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2025.04.26",
    "tags": ["Lean","Proof assistant"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/10-years/",
    "url": "https://brandonrozek.com/blog/10-years/",
    "title": "10 Years",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOn April 16 2015, I wrote my first two \u003ca href=\"/blog/2015-04-16-function-two-points-theory/\"\u003eblog\u003c/a\u003e \u003ca href=\"/blog/2015-04-16-responsive-layout-and-animation/\"\u003eposts\u003c/a\u003e on creating responsive layouts in CSS. It\u0026rsquo;s crazy to think that I still have this blog going after 10 years. Since then I\u0026rsquo;ve lived in 9 different places, worked at 6 different organizations, and got to meet many wonderful people including my now fiance.\u003c/p\u003e\n\u003cp\u003eI thought it would be nice to look back over the last ten years, and talk about my favorite blog posts of each year. What I\u0026rsquo;ve come to realize in this exercise is that I really came a long way. I mean, it\u0026rsquo;s not that hard to beat my first blog post where I just scanned a sheet of paper!\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eDisclaimer:\u003c/em\u003e Posts before mid-2019 were on WordPress, and let\u0026rsquo;s just say that the migration to Hugo hasn\u0026rsquo;t been perfect\u0026hellip;\u003c/p\u003e\n\u003ch2 id=\"2015\"\u003e2015\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eFavorite Post:\u003c/strong\u003e \u003ca href=\"/blog/2015-11-30-limiting-cache-service-workers-revisited3/\"\u003eLimiting the cache in Service Workers Revisited\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eService workers was one of the first ways you can have persistent background processing on a website. From my memory, the big use case was that you can get social media notifications without even having the tab open.  I\u0026rsquo;m not sure how, but I ended up on the bleeding edge of this since it was first fully released on Chrome without a feature flag September 2015.\u003c/p\u003e\n\u003cp\u003eWhile we all love getting notifications, a more interesting use case to us in the blogging community was that you can use service workers to make your website available offline! I wrote about \u003ca href=\"/blog/2015-11-14-service-workers/\"\u003ehow to do so\u003c/a\u003e and then wrote this update a few weeks later to help address a \u003ca href=\"https://adactio.com/journal/9844\"\u003ecaching\u003c/a\u003e \u003ca href=\"https://adactio.com/journal/9888\"\u003eissue\u003c/a\u003e that Jeremy Keith identified.\u003c/p\u003e\n\u003ch2 id=\"2016\"\u003e2016\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eFavorite Post:\u003c/strong\u003e \u003ca href=\"/blog/2016-08-16-pass-password-manager/\"\u003ePass the password manager\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eThis is the only blog post I wrote in 2016 so it wins by default. At the time, I used the CLI application \u003ca href=\"https://www.passwordstore.org/\"\u003e\u003ccode\u003epass\u003c/code\u003e\u003c/a\u003e to manage my passwords and \u003ca href=\"https://syncthing.net/\"\u003eSyncthing\u003c/a\u003e to share it between devices. Nowadays, I entrust \u003ca href=\"https://bitwarden.com/\"\u003eBitwarden\u003c/a\u003e when it comes to handling my passwords.\u003c/p\u003e\n\u003ch2 id=\"2017\"\u003e2017\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eFavorite Post:\u003c/strong\u003e \u003ca href=\"/blog/2017-03-14-monte-carlo-pi/\"\u003eApproximating Pi using a Monte Carlo Simulation\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e2017 was the year that I got really into statistics and approximation. Not only was I approximating Pi like above, but I was also testing the \u003ca href=\"https://brandonrozek.com/blog/2017-03-07-uniformity-math-random/\"\u003erandomness of random number generators\u003c/a\u003e, looking at \u003ca href=\"https://brandonrozek.com/blog/male-vs-female-life-expectancy/\"\u003emale vs female life expectancy\u003c/a\u003e, and building a regression model of the \u003ca href=\"https://brandonrozek.com/blog/albuquerque/\"\u003eprice of homes in Albuquerque, New Mexico\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eNowadays I don\u0026rsquo;t work with Statistics as often. However, I do think it\u0026rsquo;s cool how we can come up with models to attempt to describe how the world works even when we don\u0026rsquo;t have much data available.\u003c/p\u003e\n\u003cp\u003eAlso being able to make approximations with \u003ca href=\"/research/reinforcementlearning/notes/mcmethods/\"\u003eMonte-Carlo methods\u003c/a\u003e, and resampling techniques like \u003ca href=\"https://github.com/Brandon-Rozek/bootstrapr\"\u003ebootstrapping\u003c/a\u003e is awesome.\u003c/p\u003e\n\u003ch2 id=\"2018\"\u003e2018\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eFavorite Post:\u003c/strong\u003e \u003ca href=\"/blog/2018-01-22-identifying-misspelled-words-dataset-hunspell/\"\u003eIdentifying Misspelled Words in your Dataset with Hunspell\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eSimilar to 2016, this is the winner by default since it\u0026rsquo;s the only blog post I wrote that year. I\u0026rsquo;m happy that I don\u0026rsquo;t currently have to deal with data cleaning.\u003c/p\u003e\n\u003ch2 id=\"2019\"\u003e2019\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eFavorite Post:\u003c/strong\u003e  \u003ca href=\"/blog/ctf/\"\u003eCapture The Flag\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eFrom this point on, it only gets harder for me to select my favorite! For the first four years I only wrote 30 blog posts total. In the year 2019, I wrote 60! If I had to chose, my favorite would be when I briefly described the challenges I made for a capture the flag competition during my undergraduate days. I also really enjoyed encoding \u003ca href=\"/blog/pythonsymmetricgroups/\"\u003emathematical\u003c/a\u003e \u003ca href=\"/blog/haskellrealsequences/\"\u003eproblems\u003c/a\u003e in code.\u003c/p\u003e\n\u003ch2 id=\"2020\"\u003e2020\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eFavorite Post:\u003c/strong\u003e \u003ca href=\"https://brandonrozek.com/blog/discgolfpymc/\"\u003eDisk Golf and PyMC3\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eSomehow in 2020 I wrote 128 blog posts. Many of them are short tidbits of things I\u0026rsquo;ve learned. But still, that\u0026rsquo;s almost a blog post every 3 days! According to my \u003ca href=\"/stats\"\u003estats page\u003c/a\u003e this is the current record of blog posts per year by far. We\u0026rsquo;ll see if I ever beat this record.\u003c/p\u003e\n\u003cp\u003eMy favorite out of this batch is a blog post that\u0026rsquo;s a combination of personal life and statistics. In a semi-competitive semi-joking way, I wanted to determine who was the best at disk golf between Clare, Chris, and I based on just one outing. After performing some Bayesian magic, I found that Chris and I pretty much have the same performance (ignore the 0.2 score difference) and Clare is sadly worse than the both of us.\u003c/p\u003e\n\u003cp\u003eOther great pieces that year was my blog post on \u003ca href=\"/blog/cryptogames/\"\u003eCryptographic Games\u003c/a\u003e and \u003ca href=\"/blog/tcopython/\"\u003eTail call optimization in Python\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"2021\"\u003e2021\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eFavorite Post:\u003c/strong\u003e \u003ca href=\"/blog/introrfpoweramp/\"\u003eIntroduction to RF Power Amplifiers\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eBefore I stopped actively pursuing Amateur radio, I wanted to write some notes in case I ever get back into the hobby. Turns out, radio waves are based on electricity and life is a lot easier if you have an electrical engineering degree to understand all of the terminology used.\u003c/p\u003e\n\u003cp\u003eThat doesn\u0026rsquo;t mean you need a degree to get in the hobby though! In the US, you\u0026rsquo;ll need to at least have a technicians license to get on the air and luckily all the questions and answers for the exam are \u003ca href=\"https://hamexam.org/\"\u003eavailable online\u003c/a\u003e. After getting licensed, you can pick up a UHF/VHF radio for $20 and chat with anybody on a \u003ca href=\"https://www.repeaterbook.com/repeaters/\"\u003elocal repeater\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThough the reason I got into the hobby is that it can be as complicated as you want. At the time I was highly interested in digital modes and software defined radios. This blog post came about as I was trying to figure out what I needed to buy for the annual \u003ca href=\"https://en.wikipedia.org/wiki/Field_Day_%28amateur_radio%29\"\u003eAmateur Radio Field Day\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"2022\"\u003e2022\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eFavorite Post:\u003c/strong\u003e \u003ca href=\"/blog/finding-cool-people-on-mastodon/\"\u003eFinding Cool People on Mastodon\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eOn August 2022, I joined Mastodon and like all new people had to figure out where all the cool folks are. I\u0026rsquo;m not often on Mastodon nowadays, but it\u0026rsquo;s awesome that an open-source microblogging platform has gotten popular.\u003c/p\u003e\n\u003cp\u003eThis was also the year I introduced the \u003ca href=\"/tracks\"\u003etracks\u003c/a\u003e feature on my website. I really enjoy going on walks, and having a section of my website that shows where I walked and the pictures along the way is wicked cool. I fully described the implementation of this feature in the post \u003ca href=\"/blog/displaying-hikes-with-gpxstudio/\"\u003eDisplaying Hikes with gpx.studio\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eLastly, professionally I was a teaching assistant. A couple of posts were born from seeing student mistakes. My favorites were \u003ca href=\"/blog/proving-loop-invariants/\"\u003eLoop Invariants in Dafny\u003c/a\u003e and \u003ca href=\"/blog/common-mistake-induction-proofs/\"\u003eMost Common Mistake in Induction Proofs\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"2023\"\u003e2023\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eFavorite Post:\u003c/strong\u003e \u003ca href=\"/blog/adventures-in-bird-watching/\"\u003eAdventures in Bird Watching\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eIn 2021 I wrote a blog post comparing different RF power amplifiers. Similarly in 2023, I wrote about what considerations you need to make when buying binoculars for bird watching. I never fully got into bird watching as a hobby, but I encourage you to go on a bird tour at least once if you\u0026rsquo;re able. It\u0026rsquo;s fun to learn about the birds that fly above you and live in the trees near you.\u003c/p\u003e\n\u003cp\u003eI don\u0026rsquo;t often give glimpses of my life, but I shared a \u003ca href=\"/blog/day-in-brooklyn/\"\u003etrip to Brooklyn\u003c/a\u003e that Clare and I did with my brother.  In terms of more technical posts, I wrote about \u003ca href=\"/blog/functional-completeness/\"\u003efunctional completeness\u003c/a\u003e and an alternative way of \u003ca href=\"/blog/representing-uncertainty-closed-world-assumption/\"\u003erepresenting uncertainty under the closed world assumption\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eSince I work on multiple devices, I make more merge conflicts than I like to admit. I wrote a small script to tell me whether the git repository I\u0026rsquo;m working on is \u003ca href=\"/blog/ahead-behind-git/\"\u003eahead or behind\u003c/a\u003e the remote one. Lastly, for many years I\u0026rsquo;ve used Syncthing to synchronize my files between my devices. By default it uses public relays and public discovery services, however, I run my own servers so why not use that instead? I describe my setup in the blog post \u003ca href=\"/blog/private-syncthing-network/\"\u003eHow to set up a private syncthing network\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"2024\"\u003e2024\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eFavorite Post:\u003c/strong\u003e \u003ca href=\"/blog/hashing-based-on-word-emoji-lists/\"\u003eHashing Based on Word (Emoji?) Lists\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eIn contrast to what I said in the \u003ca href=\"/blog/blog-question-challenge-jan2025/\"\u003eblog questions challenge\u003c/a\u003e, my favorite blog post last year was one where I wrote a hash function based on an Emoji list. I don\u0026rsquo;t use emojis nearly enough in my daily life and there\u0026rsquo;s more than 5042 of them to chose from!\u003c/p\u003e\n\u003cp\u003eWinters here in New York is cold and I often don\u0026rsquo;t want to go outside. One of the events I look forward to is watching my university\u0026rsquo;s hockey games. Therefore, a close second place for last year is when I wrote about my \u003ca href=\"/blog/ice-hockey/\"\u003eexperience watching ice hockey\u003c/a\u003e. Another yearly tradition of Clare and I is to go \u003ca href=\"/blog/hunter-gathering-apple-picking/\"\u003eApple Picking\u003c/a\u003e in the fall.\u003c/p\u003e\n\u003cp\u003eIn terms of software, I wrote about a way you can \u003ca href=\"/blog/accessing-homelab-services-mtls/\"\u003eaccess your homelab remotely using mTLS\u003c/a\u003e. Also, I really appreciate the mission of the \u003ca href=\"/blog/tildeverse/\"\u003eTildeverse\u003c/a\u003e and last year wrote \u003ca href=\"/blog/wordguess/\"\u003eWordGuess: A game for the Tildeverse\u003c/a\u003e. Lastly, my favorite writeup related to my research last year is \u003ca href=\"/blog/safety-constraints-pddl/\"\u003eConstraints and Safety in PDDL\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eThat\u0026rsquo;s a wrap! We\u0026rsquo;re too early in the year 2025 for me to pick, as I\u0026rsquo;ve only written five blog posts so far.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s to another 10 years and as always feel \u003ca href=\"/contact/\"\u003efree to get in touch\u003c/a\u003e.\u003c/p\u003e\n",
    "date_published": "2025.04.16",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/recently-2504-april-showers/",
    "url": "https://brandonrozek.com/blog/recently-2504-april-showers/",
    "title": "Recently: April Showers Bring...",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI woke up this morning to a small layer of snow outside. In the Northeastern part of the United States, it\u0026rsquo;s said that we have \u003ca href=\"https://12seasons.nyc/\"\u003e12 seasons\u003c/a\u003e.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eWinter\u003c/li\u003e\n\u003cli\u003eFool\u0026rsquo;s Spring\u003c/li\u003e\n\u003cli\u003eSecond Winter\u003c/li\u003e\n\u003cli\u003eSpring of Deception\u003c/li\u003e\n\u003cli\u003eThird Winter\u003c/li\u003e\n\u003cli\u003eThe Pollening\u003c/li\u003e\n\u003cli\u003eActual Spring\u003c/li\u003e\n\u003cli\u003eSummer\u003c/li\u003e\n\u003cli\u003eHell\u0026rsquo;s Front Porch\u003c/li\u003e\n\u003cli\u003eFalse Fall\u003c/li\u003e\n\u003cli\u003eSecond Summer\u003c/li\u003e\n\u003cli\u003eActual Fall\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eAt the time of this post, we\u0026rsquo;re at \u0026ldquo;Third Winter\u0026rdquo;. I look forward to warm weather again soon.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cem\u003eHomelab Updates!\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eNextcloud:\u003c/strong\u003e I moved away from the Nextcloud all-in-one and instead maintain a LXC container. Unfortunately for me, the all-in-one was too much of a black box. My current deployment is significantly less stateless, but I understand it better.\u003c/p\u003e\n\u003cp\u003eWhile I was doing that, I\u0026rsquo;ve been exploring use-cases for MinIO so I thought maybe I\u0026rsquo;ll try \u003ca href=\"https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/primary_storage.html\"\u003econfiguring my object storage as the primary storage\u003c/a\u003e. However, I neglected to read the third paragraph\u0026hellip;\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eAll metadata (filenames, directory structures, etc) is stored in Nextcloud and not in the object store\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eThis makes performing backups significantly harder! So, I switched back to the normal filesystem backend for Nextcloud.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eImmich:\u003c/strong\u003e Multiple people have told me that Immich is the way to go for viewing your photos. Previously, I was scared to try it out because of the banner at the top of their page.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eThe project is under \u003cstrong\u003every active\u003c/strong\u003e development. Expect bugs and changes. Do not use it as \u003cstrong\u003ethe only way\u003c/strong\u003e to store your photos and videos.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eHowever, I have backups so what am I afraid of?\u003c/p\u003e\n\u003cp\u003eI have to say, the setup was flawless. The software is not buggy at all, and everything is snappy and works as expected.\u003c/p\u003e\n\u003cp\u003eTo import my photos, I mounted my existing directory on a \u003cem\u003edifferent\u003c/em\u003e mountpoint than the one Immich uses to store files.\u003c/p\u003e\n\u003cp\u003eExample Docker-Compose Configuration\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimmich-server\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003econtainer_name\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eimmich_server\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eimage\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eghcr.io/immich-app/immich-server:release\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eenv_file\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003e/etc/immich/immich.env\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003evolumes\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003e/home/user/immich/pictures:/usr/src/app/upload\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003e/home/user/storage/pictures:/pictures:ro\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e# Existing photo library\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003e/etc/localtime:/etc/localtime:ro\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003edepends_on\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003eimmich-redis\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003eimmich-database\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003erestart\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eunless-stopped\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Copy immich-redis and immich-database from the Github repo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# ...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen I had to log onto the Immich webapp to create a user and an API key. After that, we can  \u003ccode\u003eexec\u003c/code\u003e into the container.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker exec -it immich_server /bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLogin with the API key\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eimmich login http://localhost:2283/api \u0026lt;APIKEY\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen upload the photos\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eimmich upload --recursive /pictures\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cem\u003eWarning:\u003c/em\u003e You need enough space to have two copies of your photos. If you don\u0026rsquo;t then you\u0026rsquo;ll need to iteratively upload and delete as you go instead of doing this all at once.\u003c/p\u003e\n\u003cp\u003eAfter a bit, you should see thumbnails on the app!\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eMusic Streaming:\u003c/strong\u003e Since I listen to Japanese music, not all music services work for me. Currently I pay for YT Music, though I\u0026rsquo;ve been slowly trying to buy albums directly from Qobuz to (1) better support the artists, and (2) guarantee that it won\u0026rsquo;t disappear from my library. This is only recent, so I don\u0026rsquo;t fully endorse my workflow, but it\u0026rsquo;s been working so far and I think it\u0026rsquo;s great.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eBuy flac from \u003ca href=\"https://www.qobuz.com/us-en/shop\"\u003eQobuz\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eTransfer the files to my home server\u003c/li\u003e\n\u003cli\u003eRun \u003ca href=\"https://beets.io/\"\u003e\u003ccode\u003ebeet import\u003c/code\u003e\u003c/a\u003e to copy the music into my library with all the metadata sorted out.\u003c/li\u003e\n\u003cli\u003eUse \u003ca href=\"https://www.navidrome.org/\"\u003eNavidrome\u003c/a\u003e, an open-source server software, to provide a subsonic API to stream the music.\n\u003cul\u003e\n\u003cli\u003eIt even transcodes on the fly for you if your device doesn\u0026rsquo;t support the format it\u0026rsquo;s stored in.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eUse \u003ca href=\"https://symfonium.app/\"\u003eSymfonium\u003c/a\u003e on Android as my music app and set it to search for music on my Navidrome server.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eBoth Immich and setting up this music streaming is very new to me! So if you have any suggestions or comments about your workflow feel free to reach out :)\u003c/p\u003e\n",
    "date_published": "2025.04.12",
    "tags": ["homelab"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/ollama-cuda-podman-quadlets/",
    "url": "https://brandonrozek.com/blog/ollama-cuda-podman-quadlets/",
    "title": "Setting up Ollama with CUDA on Podman Quadlets",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://www.openwebui.com/\"\u003eOpen WebUI\u003c/a\u003e provides a nice chat interface for interacting with LLMs over Ollama and OpenAI compatible APIs. Using \u003ca href=\"https://ollama.com/\"\u003eOllama\u003c/a\u003e, we can self-host many different LLMs that are open-sourced! This post documents the steps that I took in order to get Ollama working with CUDA using my Podman setup. However given how fast Machine Learning projects iterate, I wouldn\u0026rsquo;t be surprised if these exact steps no longer work.  In that case, I\u0026rsquo;ll provide links to the official documentation which hopefully can help.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ll assume that you have the NVIDIA driver installed on your machine. The steps vary by OS/distribution and how modern of a driver you want, but I generally recommend to stick with what\u0026rsquo;s packaged in your distribution\u0026rsquo;s repository. This is to minimize headaches\u0026hellip;\u003c/p\u003e\n\u003cp\u003eWith that, our first step is to install the \u003ccode\u003envidia-container-toolkit\u003c/code\u003e.  This package contains a collection of libraries and scripts to help us run our GPU inside a container.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dnf install nvidia-container-toolkit\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAs of this time of writing, instructions for installing the toolkit can be found on \u003ca href=\"https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html\"\u003eNVIDIA\u0026rsquo;s website\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eWe can use this toolkit to generate a Common Device Interface (CDI) file which Podman will use to talk to the GPU.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo nvidia-ctk cdi generate --output\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/etc/cdi/nvidia.yaml\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eNote:\u003c/strong\u003e Every time you update your NVIDIA driver, you\u0026rsquo;ll have to run this command.\u003c/p\u003e\n\u003cp\u003eNVIDIA also documents the steps for configuring CDI on \u003ca href=\"https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/cdi-support.html#running-a-workload-with-cdi\"\u003etheir website\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eFrom here, we should make sure that the NVIDIA toolkit found the appropriate GPU(s) and has set up their CDI.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003envidia-ctk cdi list\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI only have one GPU on my machine, so it outputs something like the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eINFO[0000] Found 3 CDI devices                          \nnvidia.com/gpu=0\nnvidia.com/gpu=GPU-52785a8a-f8ca-99b9-0312-01a1f59e789b\nnvidia.com/gpu=all\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIf you want your container to be able to access all the GPUs, we can use the \u003ccode\u003envidia.com/gpu=all\u003c/code\u003e device interface. Otherwise, we can use a specific one.\u003c/p\u003e\n\u003cp\u003eThen, we restart Podman so that the CDI files are loaded.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl restart podman\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor our first test, we\u0026rsquo;ll make sure that the container can appropriately access the GPU by running the \u003ccode\u003envidia-smi\u003c/code\u003e command.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo podman run --rm \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t--device nvidia.com/gpu\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eall \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\tdocker.io/nvidia/cuda:11.0.3-base-ubuntu20.04 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\tnvidia-smi\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor my GPU it outputs:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e+-----------------------------------------------------------------------------------------+\n| NVIDIA-SMI 570.124.04             Driver Version: 570.124.04     CUDA Version: 12.8     |\n|-----------------------------------------+------------------------+----------------------+\n| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |\n| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |\n|                                         |                        |               MIG M. |\n|=========================================+========================+======================|\n|   0  NVIDIA GeForce RTX 3060        Off |   00000000:02:00.0  On |                  N/A |\n|  0%   50C    P8             19W /  170W |    1546MiB /  12288MiB |      0%      Default |\n|                                         |                        |                  N/A |\n+-----------------------------------------+------------------------+----------------------+\n                                                                                         \n+-----------------------------------------------------------------------------------------+\n| Processes:                                                                              |\n|  GPU   GI   CI              PID   Type   Process name                        GPU Memory |\n|        ID   ID                                                               Usage      |\n|=========================================================================================|\n+-----------------------------------------------------------------------------------------+\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow we are ready to set up Ollama! To save time when running our \u003ccode\u003esystemd\u003c/code\u003e commands, let\u0026rsquo;s pull the image ahead of time.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo podman pull docker.io/ollama/ollama\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe\u0026rsquo;ll have to save the models somewhere, so in this example we\u0026rsquo;ll save them to \u003ccode\u003e/opt/ollama\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mkdir /opt/ollama\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLet\u0026rsquo;s configure the Quadlet. Save the following to \u003ccode\u003e/etc/containers/systemd/ollama.container\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Container]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eContainerName\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eollama\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eHostName\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eollama\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eImage\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edocker.io/ollama/ollama\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAutoUpdate\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eregistry\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eVolume\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/opt/ollama:/root/.ollama\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePublishPort\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e11434:11434\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAddDevice\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003envidia.com/gpu=all\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRestart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ealways\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edefault.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis file specifies the flags that we pass to the podman command:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003ePublish the port 11434: This is the port we\u0026rsquo;ll use when sending messages to Ollama from Open WebUI. Of course you\u0026rsquo;re welcome to use other networking tricks to pull that off.\u003c/li\u003e\n\u003cli\u003eMount the folder \u003ccode\u003e/opt/ollama\u003c/code\u003e on the filesystem to \u003ccode\u003e/root/.ollama\u003c/code\u003e within the container: We don\u0026rsquo;t want to have to re-download the LLM models each time!\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eFor the moment of truth, let\u0026rsquo;s start it!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl start ollama\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI won\u0026rsquo;t show in this post how to configure Open WebUI, but we can make sure that everything is working by looking at the Ollama container itself.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo podman exec -it ollama /bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe\u0026rsquo;ll perform a test with a smaller model (1.2 GB):\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eollama run llama3.2:1b\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDepending on your Internet connection, this will take a couple minutes to download and load onto the GPU.\u003c/p\u003e\n\u003cp\u003eWhen it\u0026rsquo;s done the prompt will be replaced with:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e\u0026gt;\u0026gt;\u0026gt; \n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFrom here you can chat with the LLM!\u003c/p\u003e\n",
    "date_published": "2025.03.29",
    "tags": ["Ollama","Podman","CUDA"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/weather-forcecasts-cli-nws/",
    "url": "https://brandonrozek.com/blog/weather-forcecasts-cli-nws/",
    "title": "Weather Forcecasts on the Command-Line with the National Weather Service API",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe United States National Weather Service (NWS) has a \u003ca href=\"https://www.weather.gov/documentation/services-web-api\"\u003eJSON API\u003c/a\u003e which provides weather alerts and forecasts for free without having to create an API key\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e. We\u0026rsquo;ll use this resource to write a small bash script which gives today and tomorrow\u0026rsquo;s weather forecast given a latitude and longitude.\u003c/p\u003e\n\u003cp\u003eMy goal is to have a small bash script which I can run in the terminal to get the forecast information. We\u0026rsquo;ll use \u003ca href=\"https://jqlang.org/\"\u003e\u003ccode\u003ejq\u003c/code\u003e\u003c/a\u003e for handling the JSON data.\u003c/p\u003e\n\u003cp\u003eThe NWS API does not provide the forecast directly given a latitude and longitude coordinates. Instead, we need to provide the specific \u003ca href=\"https://www.weather.gov/srh/nwsoffices\"\u003eWeather Forecast Office (WFO)\u003c/a\u003e responsible for that area plus their grid definitions for the latitude and longitude coordinate.\u003c/p\u003e\n\u003cp\u003eLuckily, we can query for that information as well.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePOINTS\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003ecurl -Ls \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/points/\u003c/span\u003e$LAT\u003cspan style=\"color:#e6db74\"\u003e,\u003c/span\u003e$LON\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdetails\u003e\u003csummary\u003eFor 42.7289, -73.6915 it returns:\u003c/summary\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@context\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://geojson.org/geojson-ld/geojson-context.jsonld\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@version\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;1.1\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;wx\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/ontology#\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;s\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://schema.org/\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;geo\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;http://www.opengis.net/ont/geosparql#\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unit\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;http://codes.wmo.int/common/unit/\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@vocab\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/ontology#\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;geometry\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s:GeoCoordinates\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;geo:wktLiteral\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;city\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s:addressLocality\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;state\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s:addressRegion\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;distance\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s:Distance\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s:QuantitativeValue\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;bearing\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s:QuantitativeValue\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s:value\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s:unitCode\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;@id\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;forecastOffice\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;@id\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;forecastGridData\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;@id\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;publicZone\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;@id\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;county\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;@id\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/points/42.7289,-73.6915\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Feature\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;geometry\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Point\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;coordinates\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#ae81ff\"\u003e-73.6915\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#ae81ff\"\u003e42.7289\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;properties\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/points/42.7289,-73.6915\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wx:Point\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;cwa\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ALY\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;forecastOffice\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/offices/ALY\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;gridId\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ALY\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;gridX\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e74\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;gridY\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e67\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;forecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/gridpoints/ALY/74,67/forecast\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;forecastHourly\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/gridpoints/ALY/74,67/forecast/hourly\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;forecastGridData\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/gridpoints/ALY/74,67\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;observationStations\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/gridpoints/ALY/74,67/stations\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;relativeLocation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Feature\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;geometry\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Point\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;coordinates\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#ae81ff\"\u003e-73.707011\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#ae81ff\"\u003e42.724883\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;properties\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;city\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Watervliet\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;state\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;NY\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;distance\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wmoUnit:m\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1343.4228258697\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;bearing\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wmoUnit:degree_(angle)\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e70\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;forecastZone\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/zones/forecast/NYZ053\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;county\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/zones/county/NYC083\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;fireWeatherZone\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/zones/fire/NYZ208\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;timeZone\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;America/New_York\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;radarStation\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;KENX\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/details\u003e\u003cbr/\u003e\n\u003cp\u003eThat\u0026rsquo;s a lot of output! However, we only need three pieces of information:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eWFO office (ex: \u0026ldquo;ALY\u0026rdquo;)\u003c/li\u003e\n\u003cli\u003eGrid-X (ex: 74)\u003c/li\u003e\n\u003cli\u003eGrid-Y (ex: 67)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWe can use the following code to store this into variables using \u003ccode\u003ejq\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eOFFICE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho $POINTS | jq -r .properties.gridId\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eX\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho $POINTS | jq .properties.gridX\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eY\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho $POINTS | jq .properties.gridY\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFrom here, we can query for the weather forecast for that grid point.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eWEATHER\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003ecurl -Ls \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/gridpoints/\u003c/span\u003e$OFFICE\u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e$X\u003cspan style=\"color:#e6db74\"\u003e,\u003c/span\u003e$Y\u003cspan style=\"color:#e6db74\"\u003e/forecast\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdetails\u003e\u003csummary\u003eShow output:\u003c/summary\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@context\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://geojson.org/geojson-ld/geojson-context.jsonld\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@version\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;1.1\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;wx\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/ontology#\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;geo\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;http://www.opengis.net/ont/geosparql#\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unit\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;http://codes.wmo.int/common/unit/\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;@vocab\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/ontology#\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Feature\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;geometry\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Polygon\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;coordinates\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#ae81ff\"\u003e-73.6648\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#ae81ff\"\u003e42.7172\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#ae81ff\"\u003e-73.6602\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#ae81ff\"\u003e42.7386\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#ae81ff\"\u003e-73.6893\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#ae81ff\"\u003e42.742\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#ae81ff\"\u003e-73.694\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#ae81ff\"\u003e42.720499999999994\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#ae81ff\"\u003e-73.6648\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#ae81ff\"\u003e42.7172\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;properties\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;units\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;us\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;forecastGenerator\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;BaselineForecastGenerator\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;generatedAt\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-16T19:59:48+00:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;updateTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-16T19:38:29+00:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;validTimes\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-16T13:00:00+00:00/P8D\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;elevation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wmoUnit:m\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e61.8744\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;periods\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;number\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;This Afternoon\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;startTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-16T15:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;endTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-16T18:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;isDaytime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperature\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e69\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureUnit\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;F\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureTrend\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;probabilityOfPrecipitation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wmoUnit:percent\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windSpeed\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;22 mph\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windDirection\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;S\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/icons/land/day/wind_ovc?size=medium\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;shortForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Cloudy\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;detailedForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Cloudy, with a high near 69. South wind around 22 mph, with gusts as high as 43 mph. New rainfall amounts less than a tenth of an inch possible.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;number\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Tonight\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;startTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-16T18:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;endTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-17T06:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;isDaytime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperature\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e50\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureUnit\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;F\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureTrend\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;probabilityOfPrecipitation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wmoUnit:percent\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windSpeed\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;5 to 21 mph\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windDirection\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;SW\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/icons/land/night/tsra,100?size=medium\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;shortForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Showers And Thunderstorms\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;detailedForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;A chance of rain showers before 8pm, then showers and thunderstorms. Cloudy, with a low around 50. Southwest wind 5 to 21 mph, with gusts as high as 44 mph. Chance of precipitation is 100%. New rainfall amounts between three quarters and one inch possible.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;number\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Monday\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;startTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-17T06:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;endTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-17T18:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;isDaytime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperature\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e53\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureUnit\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;F\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureTrend\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;probabilityOfPrecipitation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wmoUnit:percent\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e60\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windSpeed\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;5 to 12 mph\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windDirection\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;NW\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/icons/land/day/rain_showers,60/rain_showers,50?size=medium\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;shortForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Rain Showers Likely\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;detailedForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Rain showers likely. Cloudy. High near 53, with temperatures falling to around 46 in the afternoon. Northwest wind 5 to 12 mph. Chance of precipitation is 60%. New rainfall amounts between a half and three quarters of an inch possible.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;number\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Monday Night\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;startTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-17T18:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;endTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-18T06:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;isDaytime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperature\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e31\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureUnit\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;F\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureTrend\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;probabilityOfPrecipitation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wmoUnit:percent\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e30\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windSpeed\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;7 to 10 mph\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windDirection\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;NW\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/icons/land/night/rain_showers,30/bkn?size=medium\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;shortForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Chance Rain Showers then Mostly Cloudy\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;detailedForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;A chance of rain showers before 8pm. Mostly cloudy, with a low around 31. Northwest wind 7 to 10 mph. Chance of precipitation is 30%. New rainfall amounts between a tenth and quarter of an inch possible.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;number\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Tuesday\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;startTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-18T06:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;endTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-18T18:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;isDaytime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperature\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e56\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureUnit\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;F\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureTrend\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;probabilityOfPrecipitation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wmoUnit:percent\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windSpeed\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;7 mph\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windDirection\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;NW\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/icons/land/day/few?size=medium\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;shortForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Sunny\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;detailedForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Sunny, with a high near 56. Northwest wind around 7 mph.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;number\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e6\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Tuesday Night\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;startTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-18T18:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;endTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-19T06:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;isDaytime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperature\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e35\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureUnit\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;F\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureTrend\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;probabilityOfPrecipitation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wmoUnit:percent\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windSpeed\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;1 to 5 mph\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windDirection\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;NE\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/icons/land/night/few?size=medium\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;shortForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Mostly Clear\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;detailedForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Mostly clear, with a low around 35.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;number\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e7\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Wednesday\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;startTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-19T06:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;endTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-19T18:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;isDaytime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperature\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e65\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureUnit\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;F\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureTrend\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;probabilityOfPrecipitation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wmoUnit:percent\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windSpeed\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2 to 9 mph\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windDirection\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;SE\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/icons/land/day/sct?size=medium\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;shortForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Mostly Sunny\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;detailedForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Mostly sunny, with a high near 65.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;number\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e8\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Wednesday Night\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;startTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-19T18:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;endTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-20T06:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;isDaytime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperature\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e45\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureUnit\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;F\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureTrend\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;probabilityOfPrecipitation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wmoUnit:percent\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windSpeed\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;9 mph\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windDirection\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;SE\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/icons/land/night/bkn?size=medium\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;shortForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Mostly Cloudy\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;detailedForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Mostly cloudy, with a low around 45.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;number\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e9\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Thursday\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;startTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-20T06:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;endTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-20T18:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;isDaytime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperature\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e61\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureUnit\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;F\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureTrend\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;probabilityOfPrecipitation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wmoUnit:percent\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e50\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windSpeed\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;8 to 14 mph\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windDirection\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;S\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/icons/land/day/bkn/rain_showers,50?size=medium\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;shortForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Mostly Cloudy then Chance Rain Showers\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;detailedForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;A chance of rain showers after 2pm. Mostly cloudy, with a high near 61. Chance of precipitation is 50%.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;number\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Thursday Night\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;startTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-20T18:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;endTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-21T06:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;isDaytime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperature\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e33\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureUnit\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;F\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureTrend\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;probabilityOfPrecipitation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wmoUnit:percent\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e70\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windSpeed\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;8 to 12 mph\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windDirection\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;SW\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/icons/land/night/rain_showers,70/snow,70?size=medium\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;shortForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Rain Showers Likely then Chance Rain And Snow Showers\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;detailedForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Rain showers likely before 2am, then a chance of rain and snow showers. Mostly cloudy, with a low around 33. Chance of precipitation is 70%.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;number\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e11\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Friday\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;startTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-21T06:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;endTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-21T18:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;isDaytime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperature\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e41\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureUnit\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;F\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureTrend\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;probabilityOfPrecipitation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wmoUnit:percent\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e50\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windSpeed\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;12 to 17 mph\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windDirection\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;NW\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/icons/land/day/snow,50/snow,30?size=medium\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;shortForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Chance Rain And Snow Showers\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;detailedForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;A chance of rain and snow showers. Partly sunny, with a high near 41. Chance of precipitation is 50%.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;number\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e12\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Friday Night\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;startTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-21T18:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;endTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-22T06:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;isDaytime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperature\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e30\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureUnit\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;F\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureTrend\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;probabilityOfPrecipitation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wmoUnit:percent\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windSpeed\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;9 to 16 mph\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windDirection\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;NW\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/icons/land/night/snow/sct?size=medium\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;shortForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Slight Chance Snow Showers then Partly Cloudy\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;detailedForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;A slight chance of snow showers before 8pm. Partly cloudy, with a low around 30.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;number\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e13\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Saturday\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;startTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-22T06:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;endTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-22T18:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;isDaytime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperature\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e52\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureUnit\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;F\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureTrend\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;probabilityOfPrecipitation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wmoUnit:percent\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windSpeed\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;9 to 13 mph\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windDirection\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;NW\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/icons/land/day/sct?size=medium\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;shortForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Mostly Sunny\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;detailedForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Mostly sunny, with a high near 52.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;number\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e14\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Saturday Night\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;startTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-22T18:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;endTime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2025-03-23T06:00:00-04:00\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;isDaytime\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperature\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e34\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureUnit\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;F\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;temperatureTrend\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;probabilityOfPrecipitation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unitCode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wmoUnit:percent\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;value\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windSpeed\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;3 to 9 mph\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;windDirection\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;NW\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/icons/land/night/bkn?size=medium\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;shortForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Mostly Cloudy\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;detailedForecast\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Mostly cloudy, with a low around 34.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/details\u003e\u003cbr/\u003e\n\u003cp\u003eThe National Weather Service packs in so much weather information in just one API call! My favorite part is that they have a \u003ccode\u003edetailedForecast\u003c/code\u003e field which provides a nice human-readable description of the weather.\u003c/p\u003e\n\u003cp\u003eI don\u0026rsquo;t know about you, but at any given time, I\u0026rsquo;m mostly curious about today and tomorrow\u0026rsquo;s weather. Unless I\u0026rsquo;m planning a trip or need to be aware of an extreme weather event, the day\u0026rsquo;s forecast mostly influences the clothes I wear.\u003c/p\u003e\n\u003cp\u003eThe final part of this script is to grab the next three \u0026ldquo;periods\u0026rdquo; and display its \u003ccode\u003edetailedForecast\u003c/code\u003e field.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e i in \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e 2; \u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$WEATHER\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | jq .properties.periods\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e$i\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e | jq -r \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e  \u0026#34;\\(.name)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e  \\(.detailedForecast)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e  \u0026#34; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eInstead of having to read an entire JSON message to get the weather, we now have a description of the forecast coming up.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eThis Afternoon\n  Cloudy, with a high near 69. South wind around 22 mph, with gusts as high as 43 mph. New rainfall amounts less than a tenth of an inch possible.\n  \nTonight\n  A chance of rain showers before 8pm, then showers and thunderstorms. Cloudy, with a low around 50. Southwest wind 5 to 21 mph, with gusts as high as 44 mph. Chance of precipitation is 100%. New rainfall amounts between three quarters and one inch possible.\n  \nMonday\n  Rain showers likely. Cloudy. High near 53, with temperatures falling to around 46 in the afternoon. Northwest wind 5 to 12 mph. Chance of precipitation is 60%. New rainfall amounts between a half and three quarters of an inch possible.\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eLook how short and sweet that output is! With that, we have all the steps needed in our script to grab the upcoming forecast using the NWS API. I created an alias called \u003ccode\u003emyweather\u003c/code\u003e so that I don\u0026rsquo;t have to memorize the latitude or longitude of where I live.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ealias myweather\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;usweather 42.7289, -73.6915\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHere is the script that I call \u003ccode\u003eusweather\u003c/code\u003e in its entirety. I placed it in \u003ccode\u003e~/.local/bin\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/sh\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# Script to query the US National Weather Forecast Office (WFO)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o errexit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o nounset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eshow_usage\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Usage: usweather [lat] [lon]\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check Argument Count\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$#\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -ne \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    show_usage\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e ! command -v jq \u0026amp;\u0026gt; /dev/null; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;jq is not installed\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# We need to figure out WFO specific parameters\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePOINTS\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003ecurl -Ls \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/points/\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e,\u003c/span\u003e$2\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eOFFICE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho $POINTS | jq -r .properties.gridId\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eX\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho $POINTS | jq .properties.gridX\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eY\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho $POINTS | jq .properties.gridY\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$OFFICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;null\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$X\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;null\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$Y\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;null\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\techo -e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Error:\\n \u003c/span\u003e$POINTS\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\texit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eWEATHER\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003ecurl -Ls \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://api.weather.gov/gridpoints/\u003c/span\u003e$OFFICE\u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e$X\u003cspan style=\"color:#e6db74\"\u003e,\u003c/span\u003e$Y\u003cspan style=\"color:#e6db74\"\u003e/forecast\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Provide the detailed forecast for the next three weather periods\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e i in \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e 2; \u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$WEATHER\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | jq .properties.periods\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e$i\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e | jq -r \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e  \u0026#34;\\(.name)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e  \\(.detailedForecast)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e  \u0026#34; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eIn this day and age, it almost feels too good to be true.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2025.03.16",
    "tags": ["Command-line interface","National Weather Service","Bash","jq"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/adventures-in-rust-webassembly/",
    "url": "https://brandonrozek.com/blog/adventures-in-rust-webassembly/",
    "title": "Adventures in Rust WebAssembly",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eFor two years now, my friend and colleague \u003ca href=\"https://jamesoswald.dev\"\u003eJames Oswald\u003c/a\u003e has told me to check out \u003ca href=\"https://webassembly.org/\"\u003eWebAssembly\u003c/a\u003e. I avoided it all this time because I thought it would be too complicated to get started with. In one of our study sessions, my friends Chris and Ethan both said they wanted to look into it. I have to say, that getting started with WebAssembly is far simpler than I\u0026rsquo;ve previously imagined and it makes me wish I listened to James earlier.\u003c/p\u003e\n\u003cp\u003eIn this post, I\u0026rsquo;ll show how to use Rust + Webassembly to create both a CLI application and a Web application. \u003ca href=\"https://rustwasm.github.io/docs/book/\"\u003eThe Rust and Webassembly Book\u003c/a\u003e is a great resource, however like \u003ca href=\"https://dzfrias.dev/blog/rust-wasm-minimal-setup/\"\u003eDiego\u003c/a\u003e, I found the recommended setup to be a bit too heavy for my liking. I\u0026rsquo;ll do anything to avoid using \u003ccode\u003enpm\u003c/code\u003e in personal projects.\u003c/p\u003e\n\u003ch3 id=\"setup\"\u003eSetup\u003c/h3\u003e\n\u003cp\u003eI assume that you have the \u003ca href=\"https://www.rust-lang.org/tools/install\"\u003e\u003ccode\u003erustup\u003c/code\u003e\u003c/a\u003e command available.\u003c/p\u003e\n\u003cp\u003eFirst, we\u0026rsquo;ll need to have the \u003ccode\u003ewasm\u003c/code\u003e toolchains installed.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eFor CLI applications: \u003ccode\u003erustup target add wasm32-wasi\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eFor Web applications: \u003ccode\u003erustup target add wasm32-unknown-unknown\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eFor the web, we\u0026rsquo;ll also need \u003ccode\u003ewasm-bindgen\u003c/code\u003e to create the JavaScript glue code which ingests the \u003ccode\u003e.wasm\u003c/code\u003e file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecargo install wasm-bindgen-cli\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow we can create our Rust project!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecargo new rustwasm\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will create the following directory structure\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003erustwasm\n├── Cargo.toml\n└── src\n    └── main.rs\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTo compile WebAssembly for the Web, edit the \u003ccode\u003eCargo.toml\u003c/code\u003e to set the crate-type and add the dependencies.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#a6e22e\"\u003epackage\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;rustwasm\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;0.1.0\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eedition\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2021\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#a6e22e\"\u003elib\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ecrate-type\u003c/span\u003e = [\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;cdylib\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;rlib\u0026#34;\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#a6e22e\"\u003edependencies\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ewasm-bindgen\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;0.2.84\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ejs-sys\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;0.3.77\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn the following Rust examples, there will be snippets of \u003ccode\u003ewasm_bindgen\u003c/code\u003e scattered about. This is to create the JavaScript glue code. If you\u0026rsquo;re only running on the CLI then those lines are not needed, but it doesn\u0026rsquo;t hurt to include them.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s add a greeter function that returns the string \u003ccode\u003e\u0026quot;Hello World\u0026quot;\u003c/code\u003e in \u003ccode\u003esrc/lib.rs\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003euse\u003c/span\u003e wasm_bindgen::prelude::\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#[wasm_bindgen]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epub\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003egreeting\u003c/span\u003e() -\u0026gt; String {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e String::from(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"cli-application\"\u003eCLI Application\u003c/h3\u003e\n\u003cp\u003eFor our CLI application, we\u0026rsquo;ll create a \u003ccode\u003emain\u003c/code\u003e function that calls our new \u003ccode\u003egreeting\u003c/code\u003e function. This will live in \u003ccode\u003esrc/main.rs\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003euse\u003c/span\u003e rustwasm;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003elet\u003c/span\u003e _args \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e std::env::args();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    println!(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e, rustwasm::greeting());\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI include the \u003ccode\u003e_args\u003c/code\u003e variable to show how you would obtain the command-line arguments, however we won\u0026rsquo;t do anything with those in this example.\u003c/p\u003e\n\u003cp\u003eTo run this in the CLI, we first need to compile the \u003ccode\u003ewasm\u003c/code\u003e file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecargo build --target wasm32-wasi --release\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen we can use \u003ca href=\"https://wasmtime.dev/\"\u003e\u003ccode\u003ewasmtime\u003c/code\u003e\u003c/a\u003e to execute the file\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewasmtime target/wasm32-wasi/release/rustwasm.wasm\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFeel free to distribute \u003ccode\u003erustwasm.wasm\u003c/code\u003e among your friends to run on their machines as well!\u003c/p\u003e\n\u003ch3 id=\"web-application\"\u003eWeb application\u003c/h3\u003e\n\u003cp\u003eNow let\u0026rsquo;s show how we would run this in the web! First let\u0026rsquo;s create a \u003ccode\u003epublic\u003c/code\u003e folder at the base of the Rust project which we\u0026rsquo;ll use to host the website code.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir public\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWithin \u003ccode\u003epublic/index.html\u003c/code\u003e include the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u0026lt;!DOCTYPE html\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003ehtml\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;\u003cspan style=\"color:#f92672\"\u003ehead\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003emeta\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003echarset\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;utf-8\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003etitle\u003c/span\u003e\u0026gt;Hello wasm!\u0026lt;/\u003cspan style=\"color:#f92672\"\u003etitle\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;/\u003cspan style=\"color:#f92672\"\u003ehead\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;\u003cspan style=\"color:#f92672\"\u003ebody\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003escript\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etype\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;module\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esrc\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;index.js\u0026#34;\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003escript\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;/\u003cspan style=\"color:#f92672\"\u003ebody\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ehtml\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cem\u003eNote the \u003ccode\u003etype=\u0026quot;module\u0026quot;\u003c/code\u003e within the script tag.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eThe JavaScript code we have to write luckily is minimal since \u003ccode\u003erust-bindgen\u003c/code\u003e does the majority of the work for us. Note that we\u0026rsquo;re not running the\u003ccode\u003emain\u003c/code\u003e function within \u003ccode\u003emain.rs\u003c/code\u003e. Instead, we only have access to the methods, structs, etc that are public and annotated with \u003ccode\u003e#[rust_bindgen]\u003c/code\u003e beforehand.\u003c/p\u003e\n\u003cp\u003eRecall in \u003ccode\u003elib.rs\u003c/code\u003e that we have the \u003ccode\u003egreeting\u003c/code\u003e function. We would incorporate that into our web application through the following code within \u003ccode\u003epublic/index.js\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eimport\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003einit\u003c/span\u003e, \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eas\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ewasm\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efrom\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;./rustwasm.js\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eawait\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003einit\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ewasm\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003egreeting\u003c/span\u003e());\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow, we need to compile our codebase and use \u003ccode\u003ewasm-bindgen\u003c/code\u003e to generate the glue code.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecargo build --target wasm32-unknown-unknown --release\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewasm-bindgen target/wasm32-unknown-unknown/release/rustwasm.wasm --out-dir public --target web\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cem\u003eNote: Everytime you update your Rust code, you\u0026rsquo;ll need to run both those commands\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eTo test this, we can quickly spin up a Python web server.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd public\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epython -m http.server\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBy default your web application would be served at https://localhost:8000. If you open up your console and refresh, you should see the \u003ccode\u003e\u0026quot;Hello World\u0026quot;\u003c/code\u003e message printed.\u003c/p\u003e\n\u003ch2 id=\"more-web-examples\"\u003eMore Web Examples\u003c/h2\u003e\n\u003cp\u003eThrough the magic of \u003ccode\u003ewasm-bindgen\u003c/code\u003e, we were able to create a function in Rust that returns a string, and without having to think about the internals of Webassembly, get a JavaScript string on the other side.\u003c/p\u003e\n\u003cp\u003eThe primary usecase of WebAssembly in my mind, is to offload heavier computations. You can edit the DOM directly using the \u003ca href=\"https://rustwasm.github.io/wasm-bindgen/api/web_sys/\"\u003e\u003ccode\u003eweb-sys\u003c/code\u003e\u003c/a\u003e crate. However, I\u0026rsquo;ll focus my remaining examples and getting data in and out of the compiled Rust code.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eExample:\u003c/strong\u003e Calling a Rust-wasm function with a primitive argument\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s create a small Rust function that takes an integer and outputs the integer plus one.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#[wasm_bindgen]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epub\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eplusone\u003c/span\u003e(x: \u003cspan style=\"color:#66d9ef\"\u003ei32\u003c/span\u003e) -\u0026gt; \u003cspan style=\"color:#66d9ef\"\u003ei32\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    x \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRemember to mark the function as public (\u003ccode\u003epub\u003c/code\u003e) and annotate it with \u003ccode\u003e#[wasm_bindgen]\u003c/code\u003e in order for it to be available in the JavaScript.\u003c/p\u003e\n\u003cp\u003eRecompile the Rust code using the two commands from earlier, and edit the \u003ccode\u003epublic/index.js\u003c/code\u003e function to log an example output of the function.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ewasm\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eplusone\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e))\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBe careful with what primitive types we use in Rust. If you were to change \u003ccode\u003ei32\u003c/code\u003e to \u003ccode\u003eu32\u003c/code\u003e and call the function in JavaScript with a negative number, then the Rust Wasm side will reinterpret the negative number as a large \u003ccode\u003eu32\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eExample:\u003c/strong\u003e Taking in arbitrary JavaScript values (Dictionaries, arrays, etc.)\u003c/p\u003e\n\u003cp\u003eUsing the \u003ca href=\"https://rustwasm.github.io/wasm-bindgen/api/js_sys/index.html\"\u003e\u003ccode\u003ejs-sys\u003c/code\u003e\u003c/a\u003e crate, we can take an arbitrary JavaScript value as a parameter and use the \u003ccode\u003eReflect\u003c/code\u003e module to access field names or interpret the data.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#[wasm_bindgen]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epub\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eget_name\u003c/span\u003e(obj: \u003cspan style=\"color:#a6e22e\"\u003eJsValue\u003c/span\u003e) -\u0026gt; String {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003elet\u003c/span\u003e key \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e JsValue::from_str(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003elet\u003c/span\u003e value_option \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e js_sys::Reflect::get(\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003eobj, \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003ekey)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        .ok()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        .and_then(\u003cspan style=\"color:#f92672\"\u003e|\u003c/span\u003ev\u003cspan style=\"color:#f92672\"\u003e|\u003c/span\u003e v.as_string());\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    value_option.unwrap_or_else(\u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e String::from(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Error encountered\u0026#34;\u003c/span\u003e))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn the example above, we take a JavaScript value called \u003ccode\u003eobj\u003c/code\u003e and attempt to access the \u003ccode\u003ename\u003c/code\u003e field as a string. Since \u003ccode\u003eobj\u003c/code\u003e can be any arbitrary JavaScript value, we need to include error handling within our code. There are three possible error conditions:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eobj\u003c/code\u003e is not a JavaScript object (within \u003ccode\u003ejs_sys::Reflect::get\u003c/code\u003e)\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e\u0026quot;name\u0026quot;\u003c/code\u003e is not a field in the object (also within \u003ccode\u003ejs_sys::Reflect::get\u003c/code\u003e)\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eobj.name\u003c/code\u003e is not a string (within \u003ccode\u003e.as_string()\u003c/code\u003e)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIt\u0026rsquo;s a little burdensome to write this code. But given that JavaScript isn\u0026rsquo;t a typed language, it\u0026rsquo;s amazing we can process arbitrary JavaScript values at all.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eExample:\u003c/strong\u003e Returning a \u003ccode\u003estruct\u003c/code\u003e from Rust-wasm\u003c/p\u003e\n\u003cp\u003eSay we have a \u003ccode\u003ePerson\u003c/code\u003e struct.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#[wasm_bindgen]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epub\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ePerson\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    name: String,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    age: \u003cspan style=\"color:#66d9ef\"\u003eu32\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn order for this struct to be accessible in the JavaScript, we\u0026rsquo;ll need to make it public (\u003ccode\u003epub\u003c/code\u003e) and add the \u003ccode\u003e#[wasm_bindgen]\u003c/code\u003e annotation.\u003c/p\u003e\n\u003cp\u003eAdditionally, JavaScript objects have constructors and getter functions. We\u0026rsquo;ll need to implement those publicly within Rust and add the right annotations.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#[wasm_bindgen]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eimpl\u003c/span\u003e Person {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e#[wasm_bindgen(constructor)]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003epub\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003enew\u003c/span\u003e(name: String, age: \u003cspan style=\"color:#66d9ef\"\u003eu32\u003c/span\u003e) -\u0026gt; \u003cspan style=\"color:#a6e22e\"\u003ePerson\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        Person { name, age }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e     \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Getter for name field\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    \u003cspan style=\"color:#66d9ef\"\u003epub\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003eself) -\u0026gt; String {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self.name.clone()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Getter for the age field\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    \u003cspan style=\"color:#66d9ef\"\u003epub\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eage\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003eself) -\u0026gt; \u003cspan style=\"color:#66d9ef\"\u003eu32\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self.age\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLastly, let\u0026rsquo;s create a small function that returns a  specific \u003ccode\u003ePerson\u003c/code\u003e struct.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#[wasm_bindgen]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epub\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ebob\u003c/span\u003e() -\u0026gt; \u003cspan style=\"color:#a6e22e\"\u003ePerson\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e Person::new(String::from(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Bob\u0026#34;\u003c/span\u003e), \u003cspan style=\"color:#ae81ff\"\u003e22\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow within \u003ccode\u003epublic/index.js\u003c/code\u003e, we can call \u003ccode\u003ewasm.bob()\u003c/code\u003e and bring over the new person object. From there, we can access its fields using the getter methods.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eperson\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ewasm\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ebob\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Name: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eperson\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e() \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;, Age: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eperson\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eage\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThat\u0026rsquo;s all for today! From here the world is our oyster. We can leverage the type safe and efficient properties of Rust and write code that\u0026rsquo;s not only available on the Web, but also through the CLI; all while being architecture-independent.\u003c/p\u003e\n",
    "date_published": "2025.02.21",
    "tags": ["WebAssembly","Rust"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/blog-question-challenge-jan2025/",
    "url": "https://brandonrozek.com/blog/blog-question-challenge-jan2025/",
    "title": "Blog Question Challenge (Jan 2025)",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eCatching up on my RSS feeds, \u003ca href=\"https://blog.avas.space/bear-blog-challenge/\"\u003eAva\u0026rsquo;s bear blog question challenge\u003c/a\u003e has been making the rounds. The last blogging trend I remember going viral was \u003ca href=\"https://defaults.rknight.me/\"\u003eApp Defaults\u003c/a\u003e inspired by an episode of \u003ca href=\"https://listen.hemisphericviews.com/097\"\u003eHemispheric Views\u003c/a\u003e back in November 2023. I felt too awkward last time around to take part, and I wouldn\u0026rsquo;t have said anything this time either, except that I got tagged by \u003ca href=\"https://www.markpitblado.me/blog/blog-question-challenge-2025\"\u003eMark Pitblado\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eNothing makes you feel more cool on the Internet than getting tagged to participate.\u003c/p\u003e\n\u003ch2 id=\"why-did-you-start-blogging\"\u003eWhy did you start blogging?\u003c/h2\u003e\n\u003cp\u003eIn the sappiest way possible, I\u0026rsquo;m not sure where I would be in life if the Internet didn\u0026rsquo;t exist. It\u0026rsquo;s really the generosity of people that share the things they\u0026rsquo;ve learned and the life they\u0026rsquo;ve lived, that helps guide me through mine.\u003c/p\u003e\n\u003cp\u003eFor this reason, blogging for me has originally served two purposes:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eTo give back to the community by sharing what I\u0026rsquo;ve learned;\u003c/li\u003e\n\u003cli\u003eTo serve as a second-brain for myself, lest I forget.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eAs time progresses, I\u0026rsquo;ve been slowly exploring blogging from a more social context. A recent addition to my website is \u003ccode\u003e/postroll\u003c/code\u003e, where I give shout-outs to \u003ca href=\"https://brandonrozek.com/postroll/\"\u003eblog posts I\u0026rsquo;ve enjoyed\u003c/a\u003e. Previously, I\u0026rsquo;ve written a couple \u003ca href=\"blog/on-earbuds/\"\u003ereply\u003c/a\u003e \u003ca href=\"/blog/re-static-plus/\"\u003eposts\u003c/a\u003e, and I\u0026rsquo;m less shy on reaching out to fellow bloggers over email.\u003c/p\u003e\n\u003cp\u003eTo the last point, I\u0026rsquo;m always ecstatic when a reader reaches out to me. \u003ca href=\"https://brandonrozek.com/contact/\"\u003eThat could be you!\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"what-platforms-are-you-using-for-your-blog-and-why\"\u003eWhat platforms are you using for your blog, and why?\u003c/h2\u003e\n\u003cp\u003eI currently use the static site generator \u003ca href=\"https://gohugo.io/\"\u003eHugo\u003c/a\u003e which takes my blog posts written in Markdown, and presents this great HTML that you\u0026rsquo;re reading right now!\u003c/p\u003e\n\u003cp\u003eMy website files are hosted on two servers.  One in New York City hosted by \u003ca href=\"https://easyvm.net/\"\u003eEasyVM\u003c/a\u003e and another in Singapore hosted by \u003ca href=\"https://greencloudvps.com/\"\u003eGreenCloud\u003c/a\u003e. The server you\u0026rsquo;re talking to right now is hopefully the one closest to you. Check out my \u003ca href=\"/blog/implementing-cdn-geodns/\"\u003eGeoDNS post\u003c/a\u003e to learn more.\u003c/p\u003e\n\u003cp\u003eFor all I know though, you dear reader are from the distant unforeseeable future. If so, hello! I can\u0026rsquo;t guarantee that anything from this section is correct anymore. This website may be \u003cem\u003equantum powered\u003c/em\u003e instead.\u003c/p\u003e\n\u003ch2 id=\"have-you-blogged-on-other-platforms-before\"\u003eHave you blogged on other platforms before?\u003c/h2\u003e\n\u003cp\u003eIn what feels like an eternity ago, my blog used to run on a self-hosted version of Wordpress. Unfortunately, the server provider I was with has faded from my memory. Exactly when the switch occurred is lost to the sands of time as well. According to the \u003ca href=\"https://web.archive.org/web/20190901000000*/brandonrozek.com\"\u003eInternet Archive\u003c/a\u003e, I started using Hugo sometime between April to July 2019.\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003cp\u003eOn a separate note, even though I don\u0026rsquo;t actively use them myself, I\u0026rsquo;m a huge fan of \u003ca href=\"/blog/tildeverse/\"\u003etildespaces\u003c/a\u003e. I first discovered them during my undergraduate days, and unfortunately that page has since been taken down. Nowadays, I currently have one at my \u003ca href=\"https://www.cs.rpi.edu/~rozekb\"\u003egraduate institution\u003c/a\u003e and you can also find me on \u003ca href=\"https://tilde.club/~brozek/\"\u003eTilde Club\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"how-do-you-write-your-posts\"\u003eHow do you write your posts?\u003c/h2\u003e\n\u003cp\u003eSince I write my posts in Markdown, I open up \u003ca href=\"https://typora.io/\"\u003eTypora\u003c/a\u003e and button smash until the words stop coming out. I use \u003ccode\u003ehugo serve\u003c/code\u003e to check that the page renders fine, and then I commit the Markdown file to my website\u0026rsquo;s \u003ca href=\"https://github.com/brandon-rozek/website\"\u003eGitHub repository\u003c/a\u003e. From there, some \u003ca href=\"/blog/deploying-hugo-website-through-gh-actions/\"\u003eautomation\u003c/a\u003e kicks off which rebuilds my website.\u003c/p\u003e\n\u003cp\u003eI like to tell myself that I \u003ca href=\"/blog/vale-linter-human-prose/\"\u003elint my blog posts\u003c/a\u003e, but let\u0026rsquo;s be real. Who likes having their computer tell them they\u0026rsquo;re wrong? Most of the time these posts escape that extra scrutiny.\u003c/p\u003e\n\u003ch2 id=\"when-do-you-feel-most-inspired-to-write\"\u003eWhen do you feel most inspired to write?\u003c/h2\u003e\n\u003cp\u003eUsually after I\u0026rsquo;ve learned or done something cool. Honestly, there\u0026rsquo;s a slight pressure for me to keep trying  new things, since the reward is that I can create a shiny new blog post. I\u0026rsquo;m far from consistent with writing. I passively collect ideas I want to play around with, and then spend an evening going through my backlog.  This results in my publishing happening in bursts.\u003c/p\u003e\n\u003ch2 id=\"do-you-normally-publish-immediately-after-writing-or-do-you-let-it-simmer-a-bit\"\u003eDo you normally publish immediately after writing, or do you let it simmer a bit?\u003c/h2\u003e\n\u003cp\u003eIn an effort to get posts out of the door, I rarely spend over one session in drafting them. I let these drafts rest a day and give them a quick proof-read when my mind is clear. Since I go through my backlog in batches, I try to keep myself to a one-post-a-day rule. I personally don\u0026rsquo;t like receiving a bunch of updates all at once, so I try my best to not subject my readers to that.\u003c/p\u003e\n\u003cp\u003eThis post happens to be an exception! I created this over \u003cem\u003etwo\u003c/em\u003e whole sessions.\u003c/p\u003e\n\u003ch2 id=\"whats-your-favorite-post-on-your-blog\"\u003eWhat\u0026rsquo;s your favorite post on your blog?\u003c/h2\u003e\n\u003cp\u003eWriting for many years now, I won\u0026rsquo;t say that I have an absolute favorite. If forced to pick from 2024, I would probably say my post on \u003ca href=\"/blog/ice-hockey/\"\u003eIce Hockey\u003c/a\u003e. It gives a little taste of my personal life while also cataloging what I\u0026rsquo;ve learn so far about the sport. My favorite technical deep-dive post of 2024 has to be \u003ca href=\"https://brandonrozek.com/blog/safety-constraints-pddl/\"\u003eConstraints and Safety in PDDL\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"any-future-plans-for-the-blog\"\u003eAny future plans for the blog?\u003c/h2\u003e\n\u003cp\u003eMy blog is coming up on its 10-year anniversary this April which is mind-boggling to me. The vast majority of my posts are technical pieces, and some are \u003ca href=\"/blog/paying-for-things/\"\u003eopinion\u003c/a\u003e \u003ca href=\"/blog/ai-fearmongering-regulatory-moat/\"\u003epieces\u003c/a\u003e, and \u003ca href=\"/blog/day-in-brooklyn/\"\u003etravel\u003c/a\u003e \u003ca href=\"/blog/circling-around-vancouver/\"\u003elogs\u003c/a\u003e. Thinking about it now, I want to add two other types of pieces to my repertoire:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eRetrospectives:\u003c/strong\u003e With the blog anniversary coming up, I\u0026rsquo;m thinking about looking at some older pieces and see how my perspective has changed over time. I\u0026rsquo;m also open to talking more about my life in general.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eState of TOPIC:\u003c/strong\u003e Back in 2019, what services did I run on my homelab? Unfortunately, I can\u0026rsquo;t recall! With all the technical posts, we can likely infer that I ran a service that I talked about.  However, it would be much simpler and likely more useful to people if I cataloged everything that I\u0026rsquo;m doing at a particcular moment for TOPIC. Currently thinking of my website and homelab.\u003c/p\u003e\n\u003ch2 id=\"who-will-participate-next\"\u003eWho will participate next?\u003c/h2\u003e\n\u003cp\u003eI tag \u003ca href=\"https://emar10.dev/\"\u003eEthan Martin\u003c/a\u003e, \u003ca href=\"https://html-chunder.neocities.org/\"\u003eZak\u003c/a\u003e, and \u003ca href=\"https://bacardi55.io/\"\u003ebacardi55\u003c/a\u003e. Hopefully you\u0026rsquo;ll take part, and if you do, I\u0026rsquo;ll link your response below.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eUpdate\u003c/strong\u003e:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://bacardi55.io/2025/01/31/blog-question-challenge-jan-2025/\"\u003ebacardi55\u0026rsquo;s response\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://html-chunder.neocities.org/blog/blogging/\"\u003eZak\u0026rsquo;s response\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://emar10.dev/posts/blog-question-challenge-jan2025/\"\u003eEthan\u0026rsquo;s response\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eMakes me wish I wrote one of those \u0026ldquo;I switched my website to Hugo!\u0026rdquo; blog posts\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2025.01.29",
    "tags": ["blogging"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/hashing-based-on-word-emoji-lists/",
    "url": "https://brandonrozek.com/blog/hashing-based-on-word-emoji-lists/",
    "title": "Hashing Based on Word (Emoji?) Lists",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen I go to download Fedora Workstation 41, it gives me an option to verify the ISO download with a SHA-256 checksum.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ea2dd3caf3224b8f3a640d9e31b1016d2a4e98a6d7cb435a1e2030235976d6da2\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe idea is that when I download the ISO, I run\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esha256sun Fedora-Workstation-Live-x86_64-41-1.4.iso\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe output of the command should match the given checksum. If not, I should assume that I have a fraudulent ISO.\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003cp\u003eSHA-256 is one particular \u003ca href=\"https://en.wikipedia.org/wiki/Cryptographic_hash_function\"\u003e\u003cem\u003eCryptographic hash function\u003c/em\u003e\u003c/a\u003e. There are many others, but these algorithms are typically used for some form of verification. These algorithms have the following properties:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eDeterministic\u003c/strong\u003e:  When you run it on the same input, you get the same output.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eFixed-Length:\u003c/strong\u003e Inputs can be as long as you want, but the output has a fixed-length.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePre-image Resistant\u003c/strong\u003e: All inputs are equally likely to produce a particular hash value.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSecond Pre-Image Resistant\u003c/strong\u003e: All inputs are equally likely to produce the same hash value as a particular input.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCollision Resistant\u003c/strong\u003e: It\u0026rsquo;s difficult to find any two messages that have the same hash value.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eI won\u0026rsquo;t get into these properties in this post. Feel free to read the Wikipedia article linked above to learn more.\u003c/p\u003e\n\u003cp\u003eAnother example of a verification task is password checking. The idea is that (hopefully most) websites don\u0026rsquo;t actually store your passwords, but a hash value of them on their servers. When you send in your password, they then hash it and make sure the hash values match.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePlease don\u0026rsquo;t use SHA-256 for password checking.\u003c/strong\u003e Unfortunately, many people use short/simple passwords, and re-use them across multiple websites. If an attacker gets a hold of your password hash database, they can run the hash function on a bunch of common passwords and see if any of those hashes match what\u0026rsquo;s inside the database. Best practices evolve over time, so please do your research when implementing authentication.\u003c/p\u003e\n\u003cp\u003eKrishna wrote a \u003ca href=\"https://chittur.dev/cs/2020/04/11/readable-hashes.html\"\u003eblog post\u003c/a\u003e a few years ago advocating for the use of \u003cem\u003ehuman-readable\u003c/em\u003e hash values. Instead of using a random sequence of characters, why don\u0026rsquo;t we use a random sequence of words instead?\u003c/p\u003e\n\u003cp\u003eAs a community, we have agreed on what possible characters a SHA-256 checksum can consist of. That is, alphanumeric characters a-z and 0-9. However, \u003cem\u003ewe don\u0026rsquo;t agree on what words we should use\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eLifting from Krishna\u0026rsquo;s blog post, there are \u003ca href=\"https://github.com/singpolyma/mnemonicode\"\u003emultiple\u003c/a\u003e \u003ca href=\"https://tools.ietf.org/html/rfc1751\"\u003ereadable\u003c/a\u003e \u003ca href=\"https://github.com/fpgaminer/hash-phrase\"\u003ehash\u003c/a\u003e ideas out there. They each use different wordlists. Even Krishna\u0026rsquo;s approach uses a different one!\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ll go over a general approach on how to implement a hash function for any arbitrary wordlist. Then I\u0026rsquo;ll argue, that we should consider using Emojis for our wordlist.\u003c/p\u003e\n\u003ch3 id=\"a-general-approach-to-hashing-with-word-lists\"\u003eA general approach to hashing with word lists\u003c/h3\u003e\n\u003cp\u003eInstead of directly worrying about how to create a hash function, we\u0026rsquo;re going to piggy-back off an existing one.  For example, consider the SHA-256 algorithm. This takes an input and produces a 256-bit number.\u003c/p\u003e\n\u003cp\u003eGiven a wordlist of $N$ words, we want to produce a sequence of words that captures at least the amount of information within a 256-bit number. The number of bits that a word represents in our wordlist is:\n$$\nwbits = \\lfloor log_2(N) \\rfloor\n$$\nFrom this, we can derive how many words we need to capture a 256-bit number\n$$\noutlen = \\lceil 256 / wbits \\rceil\n$$\nConsider an arbitrary input \u003ccode\u003ex\u003c/code\u003e and hash it to create \u003ccode\u003eh\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eh \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e int(hashlib\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esha256(x)\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehexdigest(), \u003cspan style=\"color:#ae81ff\"\u003e16\u003c/span\u003e) \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor the UTF-8 encoded string \u003ccode\u003e\u0026quot;test\u0026quot;\u003c/code\u003e, we\u0026rsquo;ll get the following binary representation:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e1001111110000110110100001000000110001000010011000111110101100101100110100010111111101010101000001100010101011010110100000001010110100011101111110100111100011011001010110000101110000010001011001101000101011101011011000001010110110000111100000000101000001000\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTo determine which word from the word lists to use, we\u0026rsquo;ll consider binary sequences of length \u003ccode\u003ewbits\u003c/code\u003e. Since 256 might not be divisible by \u003ccode\u003ewbits\u003c/code\u003e, we might need to pad a certain number of zeros at the end.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebits_needed \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003emath.ceil\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e256\u003c/span\u003e / outlen\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e * outlen\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e - \u003cspan style=\"color:#ae81ff\"\u003e256\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eh \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e h \u0026lt;\u0026lt; bits_needed\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor sake of example, let\u0026rsquo;s say that \u003ccode\u003ewbits\u003c/code\u003e is equal to 11. Then the first binary sequence is \u003ccode\u003e10011111100\u003c/code\u003e. This corresponds to the 1276th word in our word list.\u003c/p\u003e\n\u003cp\u003eIterate through all these subsequences to have a list of indices:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eindices \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e i \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e range(outlen):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    num \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (h \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u0026gt;\u003c/span\u003e (wbits \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e i)) \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003ewbits \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    indices\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend(num)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eConsider the word list \u003ccode\u003ewordlist\u003c/code\u003e, we can use the indices to print out the hashed version of our input using the word list!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewords \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [wordlist[i] \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e i \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e indices]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ejoin(words))\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe full script is located at the bottom of this post.\u003c/p\u003e\n\u003ch3 id=\"which-wordlist-should-i-use\"\u003eWhich wordlist should I use?\u003c/h3\u003e\n\u003cp\u003eThe smaller our word list length $N$ is, the more words we\u0026rsquo;ll need to output for the hash. Ideally, we use a large word list. As discussed before, there\u0026rsquo;s many different opinions on what properties a word list should have. Some believe that a word list should only contain \u003ca href=\"https://github.com/singpolyma/mnemonicode\"\u003eeasy-to-pronounce\u003c/a\u003e words. Many don\u0026rsquo;t want profanity in their word lists.\u003c/p\u003e\n\u003cp\u003eJoseph Bonneau wrote a \u003ca href=\"https://www.eff.org/deeplinks/2016/07/new-wordlists-random-passphrases\"\u003eblog post\u003c/a\u003e describing all the criteria he used when crafting his choice of 7776 words. The EFF endorses this word list for use in password generation, and the \u003ca href=\"https://github.com/ulif/diceware\"\u003ediceware\u003c/a\u003e Python package also uses this list.\u003c/p\u003e\n\u003cp\u003eWhat\u0026rsquo;s important though, is that both parties agree on what word list to use.\u003c/p\u003e\n\u003cp\u003eWhen designing a word list, one issue to look out for is the \u003ca href=\"https://en.wikipedia.org/wiki/Prefix_code\"\u003ePrefix Code Problem\u003c/a\u003e. Some words are concatenations of other words, for example: rainbow, today, sunflower, etc. If you don\u0026rsquo;t use a delimiter between words, then you\u0026rsquo;ll lose information since we can\u0026rsquo;t distinguish whether it\u0026rsquo;s multiple words or just one word.\u003c/p\u003e\n\u003cp\u003eReturning to the EFF word list, here\u0026rsquo;s how we can obtain the word list as of the time of writing:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl https://www.eff.org/files/2016/07/18/eff_large_wordlist.txt | cut -f2 \u0026gt; eff_wordlist.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn order to capture the 256-bit hash, we need to use 22 words from the EFF word list. The hash for the UTF-8 encoded version of \u0026ldquo;test\u0026rdquo; is:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eduress amiss antler atop item illicitly blimp anchor gigahertz consoling chance atonable frugality hardhead freeing bust crowd drool editor earful detective fiction\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWords are nice and all. But what about Emojis? The Unicode Consortium maintains a list of \u003ca href=\"https://unicode.org/emoji/charts/full-emoji-list.html\"\u003ethousands of emojis\u003c/a\u003e. Like words, emojis are very easy for our eyes to parse.\u003c/p\u003e\n\u003cp\u003eAs of the time of writing, we\u0026rsquo;re at version 16 of the Emoji character list.  Here\u0026rsquo;s a crude parser for getting a list of emojis.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl https://unicode.org/Public/emoji/16.0/emoji-test.txt | grep -v \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;^#\u0026#34;\u003c/span\u003e | grep -oP \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;#\\s*\\K.*?(?=\\s*E)\u0026#39;\u003c/span\u003e \u0026gt; emojis.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis produces a list of 5042 emojis. Similarly, to capture a 256-bit hash, we\u0026rsquo;ll need to use 22 emojis. For the UTF-8 encoded version of \u0026ldquo;test\u0026rdquo;, the hash is\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e🧍🏼‍♂️ 💜 🫷 🫵🏽 👩🏿‍❤‍👩🏾 👨🏼‍❤️‍👨🏼 👨🏾 💤 🤹 🧑🏿‍🎨 🤦🏼‍♂ 🫵🏼 🤸🏾 👨🏽‍❤️‍💋‍👨🏼 🚴🏿‍♂️ 🙎🏿‍♀️ 💂‍♀ 🚶🏾‍♀️‍➡️ 🧎🏽‍♀️‍➡ 🧎🏾 🧙🏿‍♂ 🏄🏻‍♂️\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eDoes your browser show all the emojis?\u003c/p\u003e\n\u003cp\u003eWhile I\u0026rsquo;m advocating for the use of emojis, there are two problems that I currently recognize:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eNew emojis get introduced regularly. Luckily the dataset is versioned, so you can state to verify using the version 16 set.\u003c/li\u003e\n\u003cli\u003eNot all applications and fonts support newer emojis. I use Konsole with Noto Sans and \u0026ldquo;🫩\u0026rdquo; is not recognized.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThough I feel that emojis are richer than a standard English based word list. Also, it\u0026rsquo;s friendly to those who don\u0026rsquo;t speak English as well!\u003c/p\u003e\n\u003cp\u003eThe full script from before:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e argparse\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e hashlib\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e math\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eparser \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e argparse\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eArgumentParser(description\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Create a hash from a wordlist\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eparser\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd_argument(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wordlist\u0026#34;\u003c/span\u003e, type\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003estr, help\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Path to wordlist\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eargs \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e vars(parser\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eparse_args())\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewordlist \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# File must have one word per line\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e open(args[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;wordlist\u0026#39;\u003c/span\u003e]) \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e f:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    wordlist \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e f\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eread()\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esplitlines()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eassert\u003c/span\u003e len(wordlist) \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Number of bits we can use to index the wordlist\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewlbits \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e math\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efloor(math\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elog(len(wordlist)) \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e math\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elog(\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eoutlen \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e math\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eceil(\u003cspan style=\"color:#ae81ff\"\u003e256\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e wlbits)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebits_needed \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (math\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eceil(\u003cspan style=\"color:#ae81ff\"\u003e256\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e outlen) \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e outlen) \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e256\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edata \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e input(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esha256_data \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e hashlib\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esha256(data\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eencode(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;utf-8\u0026#34;\u003c/span\u003e))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eencoded_data \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e int(sha256_data\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehexdigest(), \u003cspan style=\"color:#ae81ff\"\u003e16\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e bits_needed\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eindices \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e i \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e range(outlen):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    num \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (encoded_data \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u0026gt;\u003c/span\u003e (wlbits \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e i)) \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003ewlbits \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    indices\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend(num)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewords \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [wordlist[i] \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e i \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e indices]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ejoin(words))\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eYou might ask, how do we know that the checksum hasn\u0026rsquo;t been tampered with? Fedora\u0026rsquo;s solution is PGP-signatures, but that\u0026rsquo;s outside the scope of this post.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2024.12.30",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/naive-encodings-transitivity-fol/",
    "url": "https://brandonrozek.com/blog/naive-encodings-transitivity-fol/",
    "title": "Naive Encodings of Transitivity within First-Order Logic",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cblockquote\u003e\n\u003cp\u003eThe transitive closure of a binary relation cannot, in general, be expressed in first-order logic (FO)\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eIn Ray Reiter\u0026rsquo;s book \u0026ldquo;Knowledge in Action: Logical Foundations for Specifying and Implementing Dynamical Systems\u0026rdquo;, he goes over a typical naive encoding of transitivity in first-order logic and goes over a counter-example\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e.\u003c/p\u003e\n\u003cp\u003eLet $G$ be a binary relation that represents whether or not there is a direct edge between two nodes.\u003c/p\u003e\n\u003cp\u003eWe want to axiomatize the transitive closure into the binary relation $T$. This means we need to have a formula that holds when we have a transitive closure, and does not hold when we do not.\u003c/p\u003e\n\u003cp\u003eA naive axiomatization is the following:\n$$\nT(x, y) \\iff G(x, y) \\vee \\exists z(G(x, z) \\wedge T(z, y))\n$$\nRay\u0026rsquo;s counter-example is the following. Consider the following valuation of $G$:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eG: {(b, b)}\n\u003c/code\u003e\u003c/pre\u003e\n\n\u003cimg height=\"300px\" src=\"/files/images/blog/202412280944.svg\"/\u003e\n\n\u003cbr/\u003e\n\n\n\u003cp\u003eNow consider the following valuation of $T$:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eT: {(b, a), (b, b)}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe falsely state that \u003ccode\u003eb\u003c/code\u003e is connected to \u003ccode\u003ea\u003c/code\u003e via transitive closure. If our axiomatization is sound, then it should be falsified.\n$$\n\\begin{align*}\nT(b, a) \u0026amp;\\iff G(b, a) \\vee \\exists z (G(b, z) \\wedge T(z, a))  \\\\\n\u0026amp;\\impliedby \\bot \\vee (G(b, b) \\wedge T(b, a)) \\\\\n\u0026amp;\\impliedby \\bot \\vee (\\top \\wedge \\top) \\\\\n\u0026amp;\\impliedby \\top\n\\end{align*}\n$$\nHowever, the formula is satisfied! Therefore, this cannot be used to axiomatize transitive closure.\u003c/p\u003e\n\u003cp\u003eThat\u0026rsquo;s the end of the original counter-example in the book.  However, I thought it would be fun to extend the exercise.\u003c/p\u003e\n\u003cp\u003eThe issue in the last example, is that we had a cycle in which we were able to define $T(b, a)$ in terms of $T(b, a)$. What if we add a constraint so that isn\u0026rsquo;t the case?\n$$\nT(x, y) \\iff G(x, y) \\vee \\exists z(z \\ne x \\wedge G(x, z) \\wedge T(z, y))\n$$\nDoes this new formula axiomatize transitive closure? The quote at the beginning begs to differ, so let\u0026rsquo;s find a counter-example!\u003c/p\u003e\n\u003cp\u003eConsider the following valuations for $G$ and $T$:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eG: {(b, a), (a, b)}\nT: {(b, c), (a, c)}\n\u003c/code\u003e\u003c/pre\u003e\n\n\u003cimg height=\"300px\" src=\"/files/images/blog/202412281036.svg\"/\u003e\n\n\u003cbr/\u003e\n\n\n\u003cp\u003eAs before, this model should not have transitive closure. Let\u0026rsquo;s evaluate our modified formula.\n$$\n\\begin{align*}\nT(b, c) \u0026amp;\\iff G(b, c) \\vee \\exists z (z \\ne b \\wedge G(b, z) \\wedge T(z,c)) \\\\\n\u0026amp;\\impliedby \\bot \\vee (a \\ne b \\wedge G(b, a) \\wedge T(a, c)) \\\\\n\u0026amp;\\impliedby \\bot \\vee (\\top \\wedge \\top \\wedge \\top) \\\\\n\u0026amp;\\impliedby \\top\n\\end{align*}\n$$\u003c/p\u003e\n\u003cp\u003e$$\n\\begin{align*}\nT(a, c) \u0026amp;\\iff G(a, c) \\vee \\exists z (G(a, z) \\wedge T(z, c)) \\\\\n\u0026amp;\\impliedby G(a, c) \\vee (b \\ne a \\wedge G(a, b) \\wedge T(b, c)) \\\\\n\u0026amp;\\impliedby \\bot \\vee (\\top \\wedge \\top \\wedge \\top) \\\\\n\u0026amp;\\impliedby \\top\n\\end{align*}\n$$\u003c/p\u003e\n\u003cp\u003eHere we can see that the above valuations depending on each other.\u003c/p\u003e\n\u003cp\u003eWikipedia has a great \u003ca href=\"https://en.wikipedia.org/wiki/Transitive_closure\"\u003earticle\u003c/a\u003e on transitive closure including a section on its use within logic and computational complexity.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e(First-order Transitive-Closure Logic) FO(TC) is strictly more expressive than FO.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eUnfortunately there was an errata in Example 2.1.1. The set \u003ccode\u003eT\u003c/code\u003e should be described as \u003ccode\u003e{(b, a), (b, b)}\u003c/code\u003e instead of the typo \u003ccode\u003e{(a, b), (b, b)}\u003c/code\u003e. Thanks James for noticing this!\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2024.12.29",
    "tags": ["Logic"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/implementing-cdn-geodns/",
    "url": "https://brandonrozek.com/blog/implementing-cdn-geodns/",
    "title": "Rolling out my own CDN with GeoDNS",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI noticed when looking at my \u003ca href=\"https://brandonrozek.com/blog/website-status-checking/\"\u003emonitoring\u003c/a\u003e that accessing my website from Asia takes almost a full second to load.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/photo_2024-12-25_22-16-17.jpg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eI apologize to all my visitors in that segment of the green orb.  Please do write to me to tell me you exist though! I\u0026rsquo;m always happy to hear from readers of my blog.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eWhy don\u0026rsquo;t you use Cloudflare?\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eYou see friend, I believe in a decentralized web. I want to do my part in not contributing to the monopoly.\u003c/p\u003e\n\u003cp\u003eAlso, I\u0026rsquo;m not a huge fan of how most CDNs work. I don\u0026rsquo;t want to have my DNS tied to a CDN provider.\u003c/p\u003e\n\u003cp\u003eThis gets me thinking, can I roll out my own CDN? It turns out we can! For the purposes of this personal website, we don\u0026rsquo;t need servers in every country in the globe.\u003c/p\u003e\n\u003cp\u003eThe Internet as we know it, is enabled through many undersea cables. Looking at the \u003ca href=\"https://www.submarinecablemap.com/\"\u003ecable map\u003c/a\u003e and the performance timings I have, if I stick a server in Singapore, it should help bring down the load times in Japan and Austrialia as well.\u003c/p\u003e\n\u003cp\u003eMy website is entirely static. This makes keeping everything in sync simple. I only need to make sure that I have a copy of my website on both servers.\u003c/p\u003e\n\u003cp\u003eLuckily, I was able to snag a VPS in Singapore from \u003ca href=\"https://greencloudvps.com/\"\u003eGreenCloud\u003c/a\u003e for only $22/year during Black Friday.  In New York, at the time of writing I\u0026rsquo;m using \u003ca href=\"https://easyvm.net/\"\u003eEasyVM\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eNow both servers have \u003ccode\u003enginx\u003c/code\u003e installed and have the files needed to serve my website to visitors. However, if me in New York tries to connect to a server in Singapore, then I\u0026rsquo;ll be the one waiting for 874ms!\u003c/p\u003e\n\u003cp\u003eThis is where GeoDNS comes in. The DNS server analyzes the IP address of the visitor requesting my websites IP and returns the server that it thinks is the closest!\u003c/p\u003e\n\u003cp\u003eNot every DNS provider has this feature. At the time of writing, I\u0026rsquo;m using \u003ca href=\"https://bunny.net/dns/\"\u003eBunny DNS\u003c/a\u003e. The first million queries is free, and then it\u0026rsquo;s $0.30 for every million after that. For the month of December, Bunny has responded to 1,166,405 queries for my domain which luckily doesn\u0026rsquo;t break the bank for me.\u003c/p\u003e\n\u003cp\u003eIn the DNS zone, I specify the IP addresses of the two servers and include the GPS coordinates of where they sit. If you don\u0026rsquo;t know that information, you can use a website like \u003ca href=\"https://ipinfo.io/\"\u003eipinfo.io\u003c/a\u003e to find out.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/202412252243.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eWith my extra server and GeoDNS set up, I now have load times under 350ms in all the regions my monitoring service checks!\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/202412252248.png\" alt=\"\"\u003e\u003c/p\u003e\n",
    "date_published": "2024.12.28",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/curl-card/",
    "url": "https://brandonrozek.com/blog/curl-card/",
    "title": "Curl-able Business Cards",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eBrowsing around on the Interwebs, I found that \u003ca href=\"https://codingotaku.com/\"\u003eRahul\u003c/a\u003e has a curl card.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl https://codingotaku.com/cc\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e╔═══════════════════════════════════════════════════════════╗\n║                                     )                     ║\n║   (        (                     ( /(     )        )      ║\n║   )\\       )\\ ) (        (  (    )\\()) ( /(   ) ( /(   (  ║\n║ (((_)  (  (()/( )\\  (    )\\))(  ((_)\\  )\\()( /( )\\()) ))\\ ║\n║ )\\___  )\\  ((_)((_) )\\ )((_))\\    ((_)(_))/)(_)((_)\\ /((_)║\n║((/ __|((_) _| | (_)_(_/( (()(_)  / _ \\| |_((_)_| |(_(_))( ║\n║ | (__/ _ / _` | | | \u0026#39; \\)/ _` |  | (_) |  _/ _` | / /| || |║\n║  \\___\\___\\__,_| |_|_||_|\\__, |   \\___/ \\__\\__,_|_\\_\\ \\_,_|║\n║                         |___/                             ║\n║                                                           ║\n║         -~= free-software = web = anime =~-               ║\n║                                                           ║\n╠═══════════════════════════════════════════════════════════╣\n║                                                           ║\n║        ■ Web developer who adore Minimalism ■             ║\n║                                                           ║\n║     Email:  contact\u0026lt;at\u0026gt;codingotaku\u0026lt;dot\u0026gt;com                ║\n║                                                           ║\n║       Web:  https://codingotaku.com/                      ║\n║                                                           ║\n║  Peertube:  https://videos.codingotaku.com/c/codingotaku  ║\n║  Mastodon:  https://fosstodon.org/@codingotaku            ║\n║  Codeberg:  https://codeberg.org/codingotaku              ║\n║                                                           ║\n║      Card:  curl -sL https://codingotaku.com/cc           ║\n║                                                           ║\n╚═══════════════════════════════════════════════════════════╝\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe black and white rendition here really does not give it justice. Run the command yourself so you can see the wicked colors they use.\u003c/p\u003e\n\u003cp\u003eNow if you ask me, this is really awesome and I need to have this for myself. Searching around, I found an old \u003ca href=\"https://github.com/tallguyjenks/BusinessCard\"\u003eGitHub repo\u003c/a\u003e by Bryan Jenks containing the bash script they use to generate their curl card.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl https://raw.githubusercontent.com/tallguyjenks/BusinessCard/refs/heads/master/business_card\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e╭───────────────────────────────────────────────────────╮\n│                                                       │\n│           Bryan Jenks / TallGuyJenks                  │\n│                                                       │\n│     Email:  bryanjenks@protonmail.com                 │\n│       Web:  https://www.bryanjenks.dev                │\n│                                                       │\n│   Twitter:  https://twitter.com/tallguyjenks          │\n│    GitHub:  https://github.com/tallguyjenks           │\n│  LinkedIn:  https://linkedin.com/in/bryanjenks        │\n│   YouTube:  https://www.youtube.com/c/BryanJenksTech  │\n│     OrcID:  https://orcid.org/0000-0002-9604-3069     │\n│   Patreon:  https://www.patreon.com/bryanjenks        │\n│                                                       │\n│      Card:  curl -sL bit.ly/2AtVaXR                   │\n│                                                       │\n╰───────────────────────────────────────────────────────╯\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTheir bash script makes uses of \u003ccode\u003etput\u003c/code\u003e from the \u003ccode\u003encurses\u003c/code\u003e library to set the terminal colors. Adapting their script to include my information, and I now have a curl card of my own!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl https://brandonrozek.com/cc\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e╭──────────────────────────────────────────────────╮\n│                                                  │\n│                  Brandon Rozek                   │\n│                                                  │\n│     Email:  brozek@brandonrozek.com              │\n│       Web:  https://brandonrozek.com             │\n│                                                  │\n│  Mastodon:  https://fosstodon.org/@brozek        │\n│    GitHub:  https://github.com/Brandon-Rozek     │\n│                                                  │\n│      Card:  curl -sL brandonrozek.com/cc         │\n│                                                  │\n╰──────────────────────────────────────────────────╯\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2024.12.26",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/temporary-port-forward-upnp-linux/",
    "url": "https://brandonrozek.com/blog/temporary-port-forward-upnp-linux/",
    "title": "Temporary Port Forward using UPnP in Linux",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI don\u0026rsquo;t know about you, but I don\u0026rsquo;t particularly enjoy logging onto my router. \u003cem\u003eEspecially since the web UI differs between router models.\u003c/em\u003e Unfortunately, a common step of hosting a multiplayer game server at home is to set up a port forward.\u003c/p\u003e\n\u003cp\u003eThis tells your router to take traffic coming in at a specified external \u003cem\u003eport\u003c/em\u003e, and \u003cem\u003eforward\u003c/em\u003e it to one of your computer\u0026rsquo;s internal IP address at a specified local port.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/PortForward.svg\" alt=\"Port Forward Diagram\"\u003e\u003c/p\u003e\n\u003cp\u003eWhat if I tell you that instead of having to login to your router interface every time you want to set up a port forward, you can programatically perform it from your Linux machine? This is where Universal Plug and Play (UPnP) comes in. Some games even automatically manage this for you!\u003c/p\u003e\n\u003cp\u003eSince router configurations vary widely, we\u0026rsquo;ll assume that you already have UPnP enabled. Not all routers, however, support this. Look up the model of your router with UPnP to see if yours does. For example, \u003ca href=\"https://docs.netgate.com/pfsense/en/latest/services/upnp.html\"\u003ehere\u0026rsquo;s how to set it up within pfSense\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eEnabling UPnP by default means allowing any computer in your network to modify port forwarding. For security reasons, you\u0026rsquo;ll likely want to restrict this to a few trusted machines, i.e. game consoles and laptops.\u003c/p\u003e\n\u003cp\u003eIn order to manage UPnP from a Linux machine, we\u0026rsquo;ll need the \u003ccode\u003eupnpc\u003c/code\u003e command. This can be found in the \u003ccode\u003eminiupnpc\u003c/code\u003e package on Fedora.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dnf install miniupnpc\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFrom here, we can check if we\u0026rsquo;ve enabled UPnP by querying for its status\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eupnpc -s\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf it returns the following, then UPnP is not properly set up!\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eNo IGD UPnP Device found on the network !\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eOtherwise you\u0026rsquo;ll get a lot of information including:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eFound valid IGD : \u0026lt;REDACTED URL\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAssuming everything is setup correctly, we\u0026rsquo;ll run through an example. Say that we want to run a Starbound server. This typically runs on port \u003ccode\u003e21025\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport INTERNAL_PORT\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e21025\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe\u0026rsquo;ll need to know our computer\u0026rsquo;s internal IP address. Assuming that \u003ccode\u003e1.0.0.0\u003c/code\u003e will take you to the Internet and not some virtual private network, we can run the following to get our internal IP address\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport INTERNAL_IP\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eip route get \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e | awk \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;{print $7}\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor the external port that our friends will connect to, we\u0026rsquo;ll need to pick an arbitrarily high number that\u0026rsquo;s not already assigned. This number can be between 1024-65535 depending on your UPnP configuration.\u003c/p\u003e\n\u003cp\u003eFor example,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport EXTERNAL_PORT\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e60821\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLastly, we need to know whether our game server accepts TCP or UDP requests.  Starbounds expects TCP requests.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport PROTOCOL\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eTCP\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow we can issue a command to our router using \u003ccode\u003eupnpc\u003c/code\u003e to forward traffic on the external port to our local computer at the specified internal port.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eupnpc -a $INTERNAL_IP $INTERNAL_PORT $EXTERNAL_PORT $PROTOCOL\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFrom here, people outside of your network should be able to send traffic to your local computer! They\u0026rsquo;ll need to know your external IP address.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eupnpc -s | grep Ext\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf your friends are having trouble connecting to your external IP address and port, double check that your local computer\u0026rsquo;s firewall isn\u0026rsquo;t blocking the traffic and check the UPnP routing list.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eupnpc -l\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhen you\u0026rsquo;re done, don\u0026rsquo;t forget to delete the rule from UPnP.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eupnpc -d $EXTERNAL_PORT $PROTOCOL\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOther than gaming, another use case for this is file transfer.  Say you want to receive a file from a friend. To not repeat details, assume we\u0026rsquo;re using the same variables from above and you already added the port forward to UPnP.\u003c/p\u003e\n\u003cp\u003eOn your local machine, use \u003ccode\u003enetcat\u003c/code\u003e to listen for traffic and write the bytes received to a file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enc -l $INTERNAL_PORT \u0026gt; file_received.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYour friend can now send you a file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enc $EXTERNAL_IP $EXTERNAL_PORT \u0026lt; file_to_send.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2024.12.08",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/listing-offline-pages-service-workers/",
    "url": "https://brandonrozek.com/blog/listing-offline-pages-service-workers/",
    "title": "Listing Offline Pages with Service Workers",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eUsing web service workers, you can set it up so that visitors \u003ca href=\"/blog/2015-11-14-service-workers/\"\u003ehave an offline experience\u003c/a\u003e with your website. In my original blog post, I wrote about how to cache pages, and how to show them when the visitor lacks an Internet connection, or a special \u003ca href=\"/offline\"\u003eoffline page\u003c/a\u003e instead.\u003c/p\u003e\n\u003cp\u003eThis, however, doesn\u0026rsquo;t list what pages are cached on their device. Wouldn\u0026rsquo;t it make sense to show this in the offline page?  It was kinda crazy that I didn\u0026rsquo;t do this before, so in this post we\u0026rsquo;ll go over how to showcase the list of saved offline pages.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eRecall that web workers give us access to the \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/API/Cache\"\u003ecache interface\u003c/a\u003e. From it, we can access any number of caches.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eawait\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopen\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;v1::website\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eA cache is a dictionary, with \u003ccode\u003eRequests\u003c/code\u003e objects mapped to \u003ccode\u003eResponse\u003c/code\u003e objects. We can filter within our cache for request-response pairs that match a particular URL structure or has a certain content type.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eof\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eawait\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e()) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eincludes\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/blog\u0026#39;\u003c/span\u003e)) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eawait\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ematch\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;content-type\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eincludes\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;text/html\u0026#39;\u003c/span\u003e)) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eprocess\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor my offline pages listing, I want it to look like the following:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"/blog/monitoring-disks-smartattributes/\"\u003eMonitoring my Hard Drives with SMART Attributes\u003c/a\u003e (visited 20 days ago)\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/blog/adventures-in-bird-watching\"\u003eAdventures in Bird Watching\u003c/a\u003e (visited 40 days ago)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWe can get the title by searching for the \u003ccode\u003e\u0026lt;title\u0026gt;\u003c/code\u003e tag within the response object.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ebody\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003etext\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etitle\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ebody\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ematch\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e/\u0026lt;title\u0026gt;(.*)\u0026lt;\\/title\u0026gt;/\u003c/span\u003e)[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor the last visited, we can look at the response headers.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003evisited\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e Date(\u003cspan style=\"color:#a6e22e\"\u003epost\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;date\u0026#39;\u003c/span\u003e))\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLet\u0026rsquo;s say we stored all of this within a list\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eresult\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003epush\u003c/span\u003e({\u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003etitle\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003evisited\u003c/span\u003e})\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can then iterate over \u003ccode\u003eresult\u003c/code\u003e and add list items within a unordered-list \u003ccode\u003e\u0026lt;ul\u0026gt;\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eel\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e document.\u003cspan style=\"color:#a6e22e\"\u003equerySelector\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;#offline-posts\u0026#39;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eresult\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003eel\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003einnerHTML\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eresult\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      .\u003cspan style=\"color:#a6e22e\"\u003emap\u003c/span\u003e((\u003cspan style=\"color:#a6e22e\"\u003eres\u003c/span\u003e) =\u0026gt; {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003elet\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehtml\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`\u0026lt;li\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        \u0026lt;a href=\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eres\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eres\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003etitle\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026lt;/a\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        \u0026lt;small\u0026gt;\u0026lt;span title=\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eres\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003evisited\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003etoString\u003c/span\u003e()\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            (visited \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003edaysAgo\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eres\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003evisited\u003c/span\u003e)\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        \u0026lt;/span\u0026gt;\u0026lt;/small\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        \u0026lt;/li\u0026gt;`\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehtml\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      .\u003cspan style=\"color:#a6e22e\"\u003ejoin\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\\n\u0026#39;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe function \u003ccode\u003edaysAgo\u003c/code\u003e returns an easy to read time delta between the visited timestamp and now.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edaysAgo\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003edate\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003edate\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003esetHours\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etime\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edate\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003egetTime\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etoday\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e Date();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003etoday\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003esetHours\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003enow\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etoday\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003egetTime\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edelta\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e ((\u003cspan style=\"color:#a6e22e\"\u003enow\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etime\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1000\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e60\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e60\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e24\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003edelta\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;today\u0026#39;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003edelta\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e===\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;yesterday\u0026#39;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003edelta\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e days ago`\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2024.12.07",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/vale-linter-human-prose/",
    "url": "https://brandonrozek.com/blog/vale-linter-human-prose/",
    "title": "Linting my blog posts with Vale",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eHow do you write \u003cem\u003egood\u003c/em\u003e?\u003c/p\u003e\n\u003cp\u003eFor some, the answer is Grammarly. This, however, falls short to me for the following reasons:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eNeeding to rely on some third party service. Seriously, what if I want to draft my blog posts without Internet?\u003c/li\u003e\n\u003cli\u003eNot configurable. Leave me alone, sometimes I like writing \u003cstrong\u003emy way\u003c/strong\u003e.\u003c/li\u003e\n\u003cli\u003eWhere\u0026rsquo;s my beautiful terminal application?\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eI use \u003ca href=\"https://vale.sh/\"\u003eVale\u003c/a\u003e (\u003ca href=\"https://github.com/errata-ai/vale\"\u003eGitHub\u003c/a\u003e) a linter for human prose. It runs locally on my computer ✔, is configurable by defining a rule set ✔, and offers not only a beautiful CLI application ✔, but even offers integrations to editors like VSCode.\u003c/p\u003e\n\u003cp\u003eTo provide useful feedback, we\u0026rsquo;ll need a strong collection of rules. Like a crazy person, I went onto the \u003ca href=\"https://vale.sh/hub/\"\u003eVale package hub\u003c/a\u003e and looked at the rules of many different packages and compiled the ones I liked into \u003ca href=\"https://github.com/brandon-rozek/vale\"\u003emy own package\u003c/a\u003e for us to use.\u003c/p\u003e\n\u003cp\u003eAll we need to do is specify the package in our Vale config. To see where this lives, you can run \u003ccode\u003evale ls-dirs\u003c/code\u003e. For example, on my computer it is at \u003ccode\u003e~/.config/vale/.vale.ini\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eStylesPath\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/home/rozek/.local/share/vale/styles\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eVocab\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ebrozek\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eMinAlertLevel\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003esuggestion\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePackages\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ehttps://github.com/Brandon-Rozek/vale/releases/download/0.1.0/brozek.zip\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[*]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eBasedOnStyles\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eVale, brozek\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBy default, Vale includes a spell-checker. As a technical writer, I often talk about products which Vale claims are typos. We can force Vale to not complain by creating a \u003ca href=\"https://vale.sh/docs/topics/vocab/\"\u003eVocabulary\u003c/a\u003e (fancy word for dictionary).\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e\u0026lt;StylesPath\u0026gt;/config/vocabularies/brozek/accept.txt\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eBTRFS\n[Bb]oolean\nsystemd\nZulip\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThese vocabularies are case-sensitive, which while may seen like a weird choice, I find useful in keeping capitalization consistent. To specify that something is not case sensitive you\u0026rsquo;ll need to put square brackets around the upper and lower-case letter. For example, case-insensitive b is \u003ccode\u003e[Bb]\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eWith all this configured, we can then sync the configuration rules to our machine.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003evale sync\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen, lint a blog post!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003evale vale-linter-human-prose.md\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e vale-linter-human-prose.md\n 14:48  warning     Remove \u0026#39;Seriously\u0026#39; if it\u0026#39;s not  brozek.Adverbs\n                    important to the meaning of\n                    the statement.\n 20:70  suggestion  Try to keep sentences short (\u0026lt;  brozek.SentenceLength\n                    30 words).\n 48:91  warning     Remove \u0026#39;really\u0026#39; if it\u0026#39;s not     brozek.Adverbs\n                    important to the meaning of\n                    the statement.\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFrom there you can choose which suggestions to keep and which to ignore ;)\u003c/p\u003e\n",
    "date_published": "2024.11.27",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/please-monitor-disk-usage/",
    "url": "https://brandonrozek.com/blog/please-monitor-disk-usage/",
    "title": "Please monitor disk usage",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eYou know one of the worst errors to deal with on Linux?\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eNo space left on device\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eWhy? Because recovery becomes really annoying. Depending on your luck, Linux may try to cache to disk even when it\u0026rsquo;s impossible causing quite a few commands to fail.\u003c/p\u003e\n\u003cp\u003eIf you\u0026rsquo;re already in this situation, the best thing you can do is try to locate files to remove.  You can run \u003ccode\u003edu -sh *\u003c/code\u003e in any given directory to see the sizes of files and subfolders.\u003c/p\u003e\n\u003cp\u003eCommon places that hold temporary files which may be removable are:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003e/tmp\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e~/.cache\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eAn even better solution is to not get into this situation in the first place. For that, I introduce a bash script which sends a notification when the disk is getting full!\u003c/p\u003e\n\u003cp\u003eTo see the amount of available and total space for a given \u003ccode\u003e$MOUNTPOINT\u003c/code\u003e (for example, \u003ccode\u003e/\u003c/code\u003e), we run the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eavailable_space\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003edf \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$MOUNTPOINT\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | awk \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;NR==2 {print $4}\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etotal_space\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003edf \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$MOUNTPOINT\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | awk \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;NR==2 {print $2}\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAdd a couple if statements and we have ourselves a full-blown script:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/sh\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o errexit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o nounset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMAX_USAGE_PERCENT\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e90\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e -z \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$MOUNTPOINT\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;MOUNTPOINT variable not set or empty\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Get the available and total disk space for the specified mount point\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eavailable_space\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003edf \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$MOUNTPOINT\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | awk \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;NR==2 {print $4}\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etotal_space\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003edf \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$MOUNTPOINT\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | awk \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;NR==2 {print $2}\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check if the df command was successful\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e -z \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$available_space\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e -z \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$total_space\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Error: Could not retrieve disk space for \u003c/span\u003e$MOUNTPOINT\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    sendMsg \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Error: Could not retrieve disk space for \u003c/span\u003e$MOUNTPOINT\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eusage_percent\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$((\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003etotal_space \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e available_space\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e total_space \u003cspan style=\"color:#66d9ef\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e $usage_percent -ge $MAX_USAGE_PERCENT \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    host_name\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003ehostname\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Low Disk on \u003c/span\u003e$host_name\u003cspan style=\"color:#e6db74\"\u003e at mountpoint \u003c/span\u003e$MOUNTPOINT\u003cspan style=\"color:#e6db74\"\u003e. Currently using \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eusage_percent\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e% of available space.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    sendMsg \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Low Disk on \u003c/span\u003e$host_name\u003cspan style=\"color:#e6db74\"\u003e at mountpoint \u003c/span\u003e$MOUNTPOINT\u003cspan style=\"color:#e6db74\"\u003e. Currently using \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eusage_percent\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e% of available space.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Mountpoint \u003c/span\u003e$MOUNTPOINT\u003cspan style=\"color:#e6db74\"\u003e is currently using \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eusage_percent\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e% of available space.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe only part left undefined here is the \u003ccode\u003esendMsg\u003c/code\u003e function. For me, I send a \u003ca href=\"https://brandonrozek.com/blog/webhook-notifications-on-systemd-service-failure/\"\u003ewebhook notification\u003c/a\u003e to Zulip to both get notified and have a log of these messages.\u003c/p\u003e\n\u003cp\u003eTo have this check regularly automatically, we create a systemd service and timer files.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e/etc/systemd/system/lowdiskcheck.service\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eCheck for low disk space\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRequires\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enetwork-online.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWants\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eoneshot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Feel free to change the mountpoint to one that you care about\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eEnvironment\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eMOUNTPOINT=/home\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/local/bin/lowdiskcheck.sh\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003e/etc/systemd/system/lowdiskcheck.timer\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eCheck for low disk space daily\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Timer]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eOnCalendar\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edaily\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePersistent\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etimers.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen enable the timer,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl enable lowdiskcheck.timer\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2024.11.26",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/intro-possibility-theory/",
    "url": "https://brandonrozek.com/blog/intro-possibility-theory/",
    "title": "Introduction to Possibility Theory",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003ePossibility theory is a subset of Dempster-Shafer theory (DST). If you\u0026rsquo;re not already familiar with DST, I recommend you check out my \u003ca href=\"/blog/intro-dempster-shafer/\"\u003eprevious blog post\u003c/a\u003e since we\u0026rsquo;ll reuse a lot of the same concepts.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eIn Dempster-Shafer theory we start off with a mass function $m$ which is defined over all subsets of our event space $\\Omega$. When approximating $m$, we need to make $2^\\Omega$ estimates. This can be quite costly depending on the size of $\\Omega$.\u003c/p\u003e\n\u003cp\u003ePossibility theory makes the estimation of $m$ tractable at the cost of expressivity. As a subset of DST, the mass function is positive only on a sequence of nested subsets. Recall that a set represents a disjunctive observation. That is, $\\{w, g\\}$ represents $w \\vee g$.\u003c/p\u003e\n\u003cp\u003eTake for example, the following observations\n$$\nobs = c, w \\vee c, w \\vee c \\vee g\n$$\nWe can use possibility theory to reason about these since $c \\subseteq \\{w, c\\} \\subseteq \\{w, c, g\\}$ We cannot, however, use possibility theory to reason about\n$$\nobs = c, w \\vee c, g \\vee c\n$$\nsince $\\{w, c\\} \\not\\subseteq \\{g, c\\}$.\u003c/p\u003e\n\u003cp\u003eInformally, possibility theory describes uncertainty through ordering the plausibility of elementary events and constraints uncertainty such that the elementary events that are not the most plausible are never necessary.\u003c/p\u003e\n\u003ch2 id=\"properties-of-possibility-theory\"\u003eProperties of Possibility Theory\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s walk through an example so that we can illustrate different properties of possibility theory.\n$$\nobs = w \\vee c \\vee g, c, c, w \\vee c\n$$\nFrom this, we can derive the following mass function\n$$\nm(\\{c\\}) = \\frac{1}{2}, m(\\{w, c\\}) = \\frac{1}{4}, m(\\{w, c, g\\}) = \\frac{1}{4}\n$$\nThe plausibility values are as follows:\n$$\nPl(\\{g\\}) = \\frac{1}{4}, Pl(\\{w\\}) = \\frac{1}{2}, Pl(\\{c\\}) = 1 \\\\\nPl(\\{w, g\\}) = \\frac{1}{2}, Pl(\\{w, c\\}) = 1, Pl(\\{g, c\\}) = 1 \\\\\nPl(\\{w, c, g\\}) = 1\n$$\nThis leads us to the first property regarding plausibility:\n$$\nPl(A \\cup B) = max(Pl(A), Pl(B)) \\tag{0.1}\n$$\nIn order to build an intuition of this formula, let\u0026rsquo;s look at the mass function pictorally.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/focalsets.svg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eOther than the concentric rings shown, all other subsets of $\\Omega$ have a mass value of zero. When computing the plausibility value graphically, we start at the outer edge of the ring and keep summing inwards until we hit a ring that does not intersect with our set. Therefore, when it comes to the union of two sets, we keep summing until neither $A$ or $B$ intersect with the ring.\u003c/p\u003e\n\u003cp\u003eNow let\u0026rsquo;s analyze necessity:\n$$\nN(\\{w\\}) = 0, N(\\{g\\}) = 0, N(\\{c\\}) = \\frac{1}{2} \\\\\nN(\\{w, g\\}) = 0, N(\\{w, c\\}) = \\frac{3}{4}, N(\\{c, g\\}) = \\frac{1}{2} \\\\\nN(\\{w, g, c\\}) = 1\n$$\nThe necessity of two sets intersected is smallest necessity of the individual sets.\n$$\nN(A \\cap B) = min(N(A), N(B)) \\tag{0.2}\n$$\nPictorially when calculating the necessity of a set, we start from the inner circle and sum until the ring is no longer a subset of the set.\u003c/p\u003e\n\u003cp\u003eNecessity is also related to plausibility\n$$\nN(A) = 1 - Pl(\\bar{A}) \\tag{0.3}\n$$\nwhere $\\bar{A}$ is the compliment of $A$ with respect to $\\Omega$.\u003c/p\u003e\n\u003cp\u003eRecall from my last blog post that $N(\\{a\\}) = m(\\{a\\})$ for all $a \\in \\Omega$. Therefore, given the plausibility of elementary events, we can use equations 0.1 and 0.3 to uniquely determine the mass function.\u003c/p\u003e\n\u003ch2 id=\"deriving-mass-function-from-plausibility-values\"\u003eDeriving mass function from plausibility values\u003c/h2\u003e\n\u003cp\u003eConsider the following plausibility values\n$$\nPl(\\{c\\}) = 1, Pl(\\{w\\}) = b_1, Pl(\\{g\\}) = b_2\n$$\nFrom this, we can derive the other plausibility values from Equation 0.1:\n$$\nPl(\\{c, w\\}) = 1, Pl(\\{c, g\\}) = 1, Pl(\\{w, g\\}) = max(b_1, b_2) \\\\\nPl(\\{c, w, g\\}) = 1\n$$\nUsing Equation 0.3, we can derive the necessity values\n$$\nN(\\{c\\}) = 1 - max(b_1, b_2), N(\\{w\\}) = 0, N(\\{g\\}) = 0 \\\\\nN(\\{c, g\\}) = 1 - b_1, N(\\{c, w\\}) = 1 - b_2, N(\\{w, g\\}) = 0 \\\\\nN(\\{c, w, g\\}) = 1\n$$\nIn order to calculate the mass values, I often work backwards starting from smaller sets from the necessity definition.\n$$\nN(A) = \\sum_{B \\subseteq A}{m(B)}\n$$\nTherefore in our example,\n$$\nm(\\{w\\}) = 0, m(\\{g\\}) = 0 \\\\\nm(\\{c\\}) = 1 - max(b_1, b_2) \\\\\nm(\\{c, w\\}) = max(b_1, b_2) - b_2 \\\\\nm(\\{c, g\\}) = max(b_1, b_2) - b_1 \\\\\nm(\\{g, w\\}) = 0 \\\\\nm(\\{g, w, c\\}) = b_1 + b_2 - max(b_1, b_2)\n$$\nYou can verify that indeed the sum of all the mass values are equal to 1.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003ePossibility theory is a subset of Dempster-Shafer theory that makes estimation tractable. Instead of needing to come up with $2^{|\\Omega|}$ estimates to approximate the mass function, we only need to come up with $|\\Omega|$ plausiblity values in order to derive the rest of the mass function.\u003c/p\u003e\n\u003cp\u003eThis makes it a useful representation of uncertainty computationally, when we want to model imprecise sensor data or make use of qualitative or subjective measurements.\u003c/p\u003e\n",
    "date_published": "2024.10.30",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/intro-dempster-shafer/",
    "url": "https://brandonrozek.com/blog/intro-dempster-shafer/",
    "title": "Introduction to Dempster-Shafer Theory",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eImagine sitting by a tree full of birds. You know the tree only has a Yellow Rumped Warbler ($w$), a Northern Cardinal ($c$), and an American Goldfinch ($g$). These birds are respectful in that they don\u0026rsquo;t call at the same time.\u003c/p\u003e\n\u003cp\u003eYou make the following observations of bird calls:\n$$\nobs = w, w, c, g, g, w\n$$\nWhat\u0026rsquo;s the probability that we hear a Warbler next assuming the calls are independent from each other?\n$$\ncount_w = 3, total = 6, P(w) = \\frac{3}{6} = 0.5\n$$\nThis example assumes that we\u0026rsquo;re bird call experts and are able to uniquely determine each bird call. What happens if our observations are \u003cem\u003eimprecise\u003c/em\u003e?\u003c/p\u003e\n\u003ch2 id=\"dempster-shafer-theory-dst\"\u003eDempster-Shafer Theory (DST)\u003c/h2\u003e\n\u003cp\u003eDST, otherwise known as belief functions theory or the theory of evidence, looks at what happens if we allow each observation to be a disjunction ($\\vee$)?\n$$\nobs = w \\vee c \\vee g, c, w \\vee c, w \\vee g\n$$\nThere can be many reasons for this. Maybe our hearing isn\u0026rsquo;t so good. There\u0026rsquo;s additional noise around you disrupting your sensing.\u003c/p\u003e\n\u003cp\u003eFormally, let us define $\\Omega$ to be our event space. In this example, this is the set of possible bird calls.\n$$\n\\Omega = \\{w, c, g\\}\n$$\n\u003cstrong\u003eThe mass function\u003c/strong\u003e $m: A \\rightarrow [0, 1], \\forall A \\subseteq \\Omega$ assigns a value between 0 and 1 to every possible subset of our event space. The set $\\{w, g\\}$ represents the observation $w \\vee g$.\u003c/p\u003e\n\u003cp\u003eAn important property is that the sum of all the masses are equal to 1.\n$$\n\\sum_{A \\subseteq \\Omega} m(A) = 1\n$$\nIn order to derive this mass function, we can normalize our observations from earlier.\n$$\nm(\\{w, c, g\\}) = \\frac{1}{4}, m(\\{c\\}) = \\frac{1}{4}, m(\\{w, c\\}) = \\frac{1}{4}, m(\\{w, g\\}) = \\frac{1}{4}\n$$\nWe assign the value $0$ to all other subsets of $\\Omega$. By definition, $m(\\{\\}) = 0$.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThe plausibility measure\u003c/strong\u003e for a disjunctive set $A$ is the sum of all the mass values of the subets of $\\Omega$ that intersect with $A$.\n$$\nPl(A) = \\sum_{B \\cap A \\ne \\emptyset}{m(B)}\n$$\nIn our example,\n$$\n\\begin{align*}\nPl(\\{w\\}) \u0026amp;= m(\\{w, c, g\\}) + m(\\{w, c\\}) + m(\\{w,g\\}) + m(\\{w\\}) \\\\\n\u0026amp;= \\frac{3}{4}\n\\end{align*}\n$$\n\u003cstrong\u003eThe necessity measure\u003c/strong\u003e is more restrictive in that we only look at the summation of the masses of the subsets of $A$.\n$$\nNec(A) = \\sum_{B \\subseteq A}{m(B)}\n$$\nConsider an arbitrary event $a \\in \\Omega$. Then,\n$$\n\\begin{align*}\nNec(\\{a\\}) \u0026amp;= m(\\{a\\}) + m(\\{\\}) \\\\\n\u0026amp;= m(\\{a\\})\n\\end{align*}\n$$\nTherefore in our example,\n$$\nNec(\\{w\\}) = 0, Nec(\\{c\\}) = \\frac{1}{4}, Nec(\\{g\\}) = 0\n$$\nAnother example,\n$$\n\\begin{align*}\nNec(\\{w, c\\}) \u0026amp;= m(\\{w, c\\}) + m(\\{c\\}) + m(\\{w\\}) + m(\\{\\}) \\\\\n\u0026amp;= \\frac{1}{4} + \\frac{1}{4} + 0 + 0 \\\\\n\u0026amp;= 0.5\n\\end{align*}\n$$\n\u003cstrong\u003eThe probability measure\u003c/strong\u003e is bounded by the necessity and plausibility measures.\u003c/p\u003e\n\u003cp\u003eFor a disjunctive set $A$,\n$$\nNec(A) \\le P(A) \\le Pl(A)\n$$\nExtending probability to a range of values gives us a way to model \u003cem\u003eignorance\u003c/em\u003e. We say an agent is completely ignorant if $|\\Omega| \u0026gt; 1$ and $m(\\Omega) = 1$.\u003c/p\u003e\n\u003cp\u003eConsider a completely ignorant agent where $\\Omega = \\{w, c, g\\}$.\u003c/p\u003e\n\u003cp\u003eThen,\n$$\n\\begin{align*}\nNec(\\{w\\}) \\le P(\\{w\\}) \u0026amp;\\le Pl(\\{w\\}) \\\\\nm(\\{w\\}) \\le P(\\{w\\}) \u0026amp;\\le m(\\{w\\}) + m(\\{w, c\\}) + m(\\{w, g\\}) + m(\\{w, c, g\\}) \\\\\n0 \\le P(\\{w\\}) \u0026amp;\\le 1\n\\end{align*}\n$$\nProbability theory is a subset of Dempster-Shafer theory. In order to see this, let us look at an example of observations where there is no disjunction.\n$$\nobs = w, w, c, g, g, w\n$$\nNormalize our observations to derive the mass function:\n$$\nm(\\{w\\}) = \\frac{1}{2}, m(\\{c\\}) = \\frac{1}{6}, m(\\{g\\}) = \\frac{1}{3}\n$$\nThe mass function in this example is $0$ for every non-singleton subset of $\\Omega$.\u003c/p\u003e\n\u003cp\u003eWhat is the probability range for $w$?\n$$\n\\begin{align*}\nNec(\\{w\\}) \\le P(\\{w\\}) \u0026amp;\\le Pl(\\{w\\}) \\\\\nm(\\{w\\}) \\le P(\\{w\\}) \u0026amp;\\le m(\\{w\\}) + m(\\{w, g\\}) + m(\\{w, c\\}) + m(\\{w, c, g\\}) \\\\\n\\frac{1}{2} \\le P(\\{w\\}) \u0026amp;\\le \\frac{1}{2} + 0 + 0 + 0\n\\end{align*}\n$$\nTherefore, $P(w) = \\frac{1}{2}$ as expected in probability theory.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eDempster-Shafer theory is an attempt at addressing \u003cem\u003eimprecise observations\u003c/em\u003e through disjunctive events. It extends probability theory to consider not just a single value, but a range of possible values. This allows the model to decouple uncertainty from \u003cem\u003eignorance\u003c/em\u003e.\u003c/p\u003e\n",
    "date_published": "2024.10.29",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/replacing-drive-btrfs/",
    "url": "https://brandonrozek.com/blog/replacing-drive-btrfs/",
    "title": "Replacing a drive in Btrfs",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe following are the steps that I take to replace a hard drive in Btrfs with a drive of equal capacity or larger. These instructions are mostly taken from \u003ca href=\"https://wiki.tnonline.net/w/Btrfs/Replacing_a_disk\"\u003eForza\u0026rsquo;s Wiki\u003c/a\u003e which goes into much more depth.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eStep 1\u003c/strong\u003e: Setup a USB with a live image\u003c/p\u003e\n\u003cp\u003eIt turns out that with a hard drive missing, my system would refuse to boot. To get around this, we\u0026rsquo;ll need to use a live image to get to a terminal which will allow us to run the commands we need.\u003c/p\u003e\n\u003cp\u003eI did this by grabbing the latest version of \u003ca href=\"https://fedoraproject.org/server/download/\"\u003eFedora Server\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eStep 2\u003c/strong\u003e Get to a terminal\u003c/p\u003e\n\u003cp\u003eIf we used the Fedora server image, we\u0026rsquo;ll need to select \u0026ldquo;Rescue Fedora system\u0026rdquo;. If it fails to boot normally, then it\u0026rsquo;ll also fail at this step but will give us access to a terminal which is what we want.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eStep 3\u003c/strong\u003e: Mount the Btrfs file system as degraded\u003c/p\u003e\n\u003cp\u003eThe Btrfs tooling expects that the filesystem is mounted. Assuming that \u003ccode\u003e/dev/sda1\u003c/code\u003e is one of the partitions already in the Btrfs pool, we can do the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir /mnt/btrfs\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emount -o degraded /dev/sda1 /mnt/btrfs\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eStep 4\u003c/strong\u003e: Prepare the new disk (Optional)\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s assume that \u003ccode\u003e/dev/sdX\u003c/code\u003e is our new disk. We can set up a \u003ccode\u003egpt\u003c/code\u003e partition table using \u003ccode\u003eparted\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eOpen up the parted tool: \u003ccode\u003eparted /dev/sdX\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003emklabel gpt\nmkpart primary btrfs 4MiB 100%\nquit\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003eStep 5:\u003c/strong\u003e Identify missing device ID\u003c/p\u003e\n\u003cp\u003eSince we\u0026rsquo;re replacing a hard drive, this means that one of the device ids is missing in the pool. We can use the following command to see which one\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebtrfs filesystem show\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eStep 6:\u003c/strong\u003e Start the replacement process\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s say that the missing device id is \u003ccode\u003eY\u003c/code\u003e. We can then replace that device with our new drive with the following command:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebtrfs replace start Y /dev/sdX /mnt/btrfs\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will take some time. We can check the status with:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebtrfs replace status /mnt/btrfs\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eStep 7\u003c/strong\u003e: Resize the filesystem (Optional)\u003c/p\u003e\n\u003cp\u003eIf we replaced the disk with a larger one, we\u0026rsquo;ll need to tell Btrfs the new size to take advantage of it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebtrfs filesystem resize Y:max /mnt/btrfs\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDon\u0026rsquo;t forget to replace \u003ccode\u003eY\u003c/code\u003e with your device id. Also, \u003cem\u003emax\u003c/em\u003e is a special keyword that btrfs recognizes so we don\u0026rsquo;t need to replace it.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eStep 8\u003c/strong\u003e: Rebalance the filesystem\u003c/p\u003e\n\u003cp\u003eIn order to ensure that our data is evenly distributed across the available hard drives, we\u0026rsquo;ll need to re balance the btrfs array. Warning, this will take a very long time. I think of this as an overnight task.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebtrfs balance start /mnt/btrfs\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThat\u0026rsquo;s it! Restart the computer and make sure that it boots properly. Then, enjoy.\u003c/p\u003e\n",
    "date_published": "2024.10.23",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/monitoring-disks-smartattributes/",
    "url": "https://brandonrozek.com/blog/monitoring-disks-smartattributes/",
    "title": "Monitoring my Hard Drives with SMART Attributes",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eAfter having a hard drive fail on me once, I\u0026rsquo;ve been slowly upgrading my homelab to be more resilient. Currently I take daily backups using \u003ca href=\"https://restic.net/\"\u003eRestic\u003c/a\u003e, push it offsite to two different services, and have 4 hard drives \u003ca href=\"https://brandonrozek.com/blog/switching-raid-10/\"\u003eset up in a RAID 10 configuration\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThe RAID 10 configuration means that I can safely lose one hard drive without needing to access my backups. Though you know the saying, \u0026ldquo;when it rains it pours\u0026rdquo;. This means I can\u0026rsquo;t ignore the possibility that multiple hard drives die at once.\u003c/p\u003e\n\u003cp\u003eLuckily, in many cases, we can detect early signs of drive failures. This is where SMART attributes come in. The \u003cstrong\u003eS\u003c/strong\u003eelf-\u003cstrong\u003eM\u003c/strong\u003eonitoring, \u003cstrong\u003eA\u003c/strong\u003enalysis, and \u003cstrong\u003eR\u003c/strong\u003eeporting \u003cstrong\u003eT\u003c/strong\u003eechnology (SMART) system reports many different indicators on drive reliability. Unfortunately, which indicators the hard drive reports, and sometimes even the way the value is formatted is vendor-dependent.\u003c/p\u003e\n\u003cp\u003eIn order to list the value of all the SMART attributes of a given drive (e.g \u003ccode\u003e/dev/sda\u003c/code\u003e),\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo smartctl -A /dev/sda\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWikipedia maintains a list of \u003ca href=\"https://en.wikipedia.org/wiki/Self-Monitoring%2C_Analysis_and_Reporting_Technology#Known_ATA_S.M.A.R.T._attributes\"\u003ecommon SMART attributes\u003c/a\u003e. In my server, I have a mix of Western Digital and Seagate drives.  I looked at the SMART attributes that were in common between these drives, and additionally filtered by the ones where the values are vendor-agnostic. This gives me the following table:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eAttribute\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eReallocated_Sector_Ct\u003c/td\u003e\n          \u003ctd\u003eThe number of bad sectors that have been found and reallocated.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eCurrent_Pending_Sector\u003c/td\u003e\n          \u003ctd\u003eThe number of sectors waiting to be remapped due to unrecoverable read errors.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eOffline_Uncorrectable\u003c/td\u003e\n          \u003ctd\u003eThe total number of uncorrectable errors when reading/writing a sector.\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eIn order to grab the value of a specific SMART attribute, we need to filter the \u003ccode\u003esmartctl\u003c/code\u003e output with \u003ccode\u003egrep\u003c/code\u003e and \u003ccode\u003eawk\u003c/code\u003e,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo smartctl -A /dev/sda | grep Reallocated_Sector_Ct | awk \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;{print $NF}\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHopefully, the last command printed \u003ccode\u003e0\u003c/code\u003e for you\u0026hellip;\u003c/p\u003e\n\u003cp\u003eNow that we know which attributes to make sure stay zero and we have a way to \u003ca href=\"https://brandonrozek.com/blog/webhook-notifications-on-systemd-service-failure/\"\u003eget notified from our server\u003c/a\u003e, we can create a script that runs daily and notifies us only when a bad sector appears.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Sends a webhook based on the argument given\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# feel free to replace this with your own\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# solution\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esendMsg\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local MSG\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local CLEAN_MSG\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    CLEAN_MSG\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$MSG\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | jq -Rsa .\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    curl -X POST --data-urlencode \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;payload={\\\u0026#34;text\\\u0026#34;: \u003c/span\u003e$CLEAN_MSG\u003cspan style=\"color:#e6db74\"\u003e}\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$WEBHOOK_URL\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDifferent failure modes:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eDrive that we expect exists doesn\u0026rsquo;t. It\u0026rsquo;s likely too late for the drive at this point\u0026hellip;\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esendMissingDevice\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local DEVICE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local MSG\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ALERT: \u0026#39;\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#39; not found\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    sendMsg \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$MSG\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col start=\"2\"\u003e\n\u003cli\u003eThe hard drive does not report one of the three smart attributes we\u0026rsquo;re checking. In this case, you\u0026rsquo;ll either have to skip the attribute check for this drive or find another common set of attributes to check for.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esendMissingAttribute\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local DEVICE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local ATTRIBUTE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local MSG\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ALERT: \u0026#39;\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#39; is missing attribute \u0026#39;\u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    sendMsg \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$MSG\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col start=\"3\"\u003e\n\u003cli\u003eThe indicator reports a non-zero value. For the attributes we\u0026rsquo;re monitoring, this means that we have a bad sector in our hard drive.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esendAlert\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local DEVICE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local ATTRIBUTE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local RAW_VALUE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$3\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local MSG\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;WARNING: \u0026#39;\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#39; has a non-zero raw value for attribute \u0026#39;\u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;.\\n\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e \u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e \u003c/span\u003e$RAW_VALUE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    sendMsg \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$MSG\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOur main loop then iterates over all our devices and attributes we want to check for.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e DEVICE in \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eDEVICES[@]\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Checking \u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e...\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Check if the device exists\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e ! -e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Device \u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e not found.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        sendMissingDevice \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003econtinue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    SMART_OUTPUT\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003esmartctl -A \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e ATTRIBUTE in \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eATTRIBUTES[@]\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# Check if the attribute exists in the output\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e ! echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$SMART_OUTPUT\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | grep -q \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Attribute \u0026#39;\u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e\u0026#39; not found\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            sendMissingAttribute \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003econtinue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        RAW_VALUE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$SMART_OUTPUT\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | grep \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | awk \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;{print $NF}\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$RAW_VALUE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -gt \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Attribute \u0026#39;\u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e\u0026#39; has raw value of \u0026#39;\u003c/span\u003e$RAW_VALUE\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            sendAlert \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$RAW_VALUE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003ePut this all in a script located at \u003ccode\u003e/usr/local/bin/monitor-disks.sh\u003c/code\u003e. In order to have this script run daily, we\u0026rsquo;ll need to first create a systemd service at \u003ccode\u003e/etc/systemd/system/monitor-disks.service\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eMonitors disks for bad sectors\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRequires\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWants\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# You\u0026#39;ll want to run this script after all the\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# hard drives come online\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAfter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edev-sda1.device dev-sdb1.device dev-sdc1.device dev-sdd1.device\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# See: https://brandonrozek.com/blog/webhook-notifications-on-systemd-service-failure/\t\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eOnFailure\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ewebhook-notify@%i.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eoneshot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/local/bin/monitor-disks.bash\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFinally, we create a systemd timer at \u003ccode\u003e/etc/systemd/system/monitor-disks.timer\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eCheck for bad sectors daily\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Timer]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eOnCalendar\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edaily\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePersistent\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etimers.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor your convenience, the \u003ccode\u003emonitor-disks.sh\u003c/code\u003e file in its entirety:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDEVICES\u003cspan style=\"color:#f92672\"\u003e=(\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/dev/sda\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/dev/sdb\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/dev/sdc\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/dev/sdd\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eATTRIBUTES\u003cspan style=\"color:#f92672\"\u003e=(\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Reallocated_Sector_Ct\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Current_Pending_Sector\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Offline_Uncorrectable\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eWEBHOOK_URL\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;INSERT_WEBHOOK_URL_HERE\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$EUID\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -ne \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Please run as root\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  exit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e ! command -v smartctl \u0026amp;\u0026gt; /dev/null; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;smartctl is not installed\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e ! command -v jq \u0026amp;\u0026gt; /dev/null; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;jq is not installed\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esendMsg\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local MSG\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local CLEAN_MSG\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    CLEAN_MSG\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$MSG\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | jq -Rsa .\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    curl -X POST --data-urlencode \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;payload={\\\u0026#34;text\\\u0026#34;: \u003c/span\u003e$CLEAN_MSG\u003cspan style=\"color:#e6db74\"\u003e}\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$WEBHOOK_URL\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esendMissingDevice\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local DEVICE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local MSG\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ALERT: \u0026#39;\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#39; not found\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    sendMsg \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$MSG\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esendMissingAttribute\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local DEVICE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local ATTRIBUTE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local MSG\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ALERT: \u0026#39;\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#39; is missing attribute \u0026#39;\u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    sendMsg \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$MSG\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esendAlert\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local DEVICE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local ATTRIBUTE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local RAW_VALUE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$3\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    local MSG\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;WARNING: \u0026#39;\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#39; has a non-zero raw value for attribute \u0026#39;\u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;.\\n\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e \u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e \u003c/span\u003e$RAW_VALUE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    sendMsg \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$MSG\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e DEVICE in \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eDEVICES[@]\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Checking \u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e...\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Check if the device exists\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e ! -e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Device \u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e not found.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        sendMissingDevice \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003econtinue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    SMART_OUTPUT\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003esmartctl -A \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e ATTRIBUTE in \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eATTRIBUTES[@]\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# Check if the attribute exists in the output\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e ! echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$SMART_OUTPUT\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | grep -q \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Attribute \u0026#39;\u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e\u0026#39; not found\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            sendMissingAttribute \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003econtinue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        RAW_VALUE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$SMART_OUTPUT\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | grep \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | awk \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;{print $NF}\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$RAW_VALUE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -gt \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Attribute \u0026#39;\u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e\u0026#39; has raw value of \u0026#39;\u003c/span\u003e$RAW_VALUE\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            sendAlert \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$DEVICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$ATTRIBUTE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$RAW_VALUE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2024.10.17",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/jq/",
    "url": "https://brandonrozek.com/blog/jq/",
    "title": "Process JSON in the terminal with jq",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe \u003ccode\u003ejq\u003c/code\u003e command is great for quickly viewing and manipulating JSON data. By default, the output is formatted is a human-readable way, and they provide an easy way to \u0026ldquo;filter\u0026rdquo; or access elements within the JSON data.\u003c/p\u003e\n\u003cp\u003eFor example\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{\\\u0026#34;firstName\\\u0026#34;: \\\u0026#34;Brandon\\\u0026#34;, \\\u0026#34;lastName\\\u0026#34;: \\\u0026#34;Rozek\\\u0026#34;}\u0026#34;\u003c/span\u003e  | jq\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOutputs\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;firstName\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;lastName\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Rozek\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo see what\u0026rsquo;s in the field \u003ccode\u003efirstName\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{\\\u0026#34;firstName\\\u0026#34;: \\\u0026#34;Brandon\\\u0026#34;, \\\u0026#34;lastName\\\u0026#34;: \\\u0026#34;Rozek\\\u0026#34;}\u0026#34;\u003c/span\u003e  | jq .firstName\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOther than quickly viewing JSON objects in the terminal. I have two other use cases for it.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e1: Sanitizing Strings\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho $OUTPUT | jq -Rsa .\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eFlag\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-R\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eDon´t parse the input as JSON. Instead, each line of text is passed to the filter as a string.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-s\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003ePass the entire input to the filter as a single long string\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-a\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eProduce  pure  ASCII  output  with every  non-ASCII  character replaced with the equivalent escape sequence.\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e\u003cstrong\u003e2: Stringifying JSON\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ejq \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;.|tojson\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFrom the man pages\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eThe \u003ccode\u003etojson\u003c/code\u003e and \u003ccode\u003efromjson\u003c/code\u003e builtins dump values as JSON texts or parse JSON texts into values, respectively.\u003c/p\u003e\u003c/blockquote\u003e\n",
    "date_published": "2024.10.05",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/prost-import-external-schemas/",
    "url": "https://brandonrozek.com/blog/prost-import-external-schemas/",
    "title": "Importing External Schemas in Prost",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://github.com/tokio-rs/prost\"\u003eProst\u003c/a\u003e is a popular Protocol Buffers implementation written in Rust. The \u003ccode\u003eprotoc\u003c/code\u003e command is commonly used to generate serializers and de-serializers for many different languages. However, the Prost team instead recommends compiling the Protocol Buffers schema directly within \u003ccode\u003ebuild.rs\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eIn this post, I\u0026rsquo;ll give a quick tutorial on how I compiled my Protocol Buffers schema when I needed to depend on another package with its own PB messages.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s say I have a Rust package called \u003ccode\u003epeople\u003c/code\u003e. It contains a file called \u003ccode\u003ePerson.proto\u003c/code\u003e with the following contents:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-protobuf\" data-lang=\"protobuf\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esyntax \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;proto3\u0026#34;\u003c/span\u003e;\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003epackage\u003c/span\u003e people;\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003emessage\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eEmployee\u003c/span\u003e {\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e name \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e position \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e;\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u003c/span\u003e}\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor sake of our example, we\u0026rsquo;ll assume they exposed the data structures via a \u003ccode\u003eproto\u003c/code\u003e submodule. However, you can check what the module name actually is by looking at the package\u0026rsquo;s codebase for something like\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epub\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003emod\u003c/span\u003e namespace_name_goes_here {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e#![allow(missing_docs)]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    include!(concat!(env!(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;OUT_DIR\u0026#34;\u003c/span\u003e), \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/people.rs\u0026#34;\u003c/span\u003e));\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow to the package we\u0026rsquo;re creating (which I\u0026rsquo;ll call \u003ccode\u003ecompany\u003c/code\u003e), First, make sure that you added the \u003ccode\u003epeople\u003c/code\u003e package to the Cargo.toml.\u003c/p\u003e\n\u003cp\u003eThen say that we have the following file \u003ccode\u003eschemas/Team.proto\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-protobuf\" data-lang=\"protobuf\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esyntax \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;proto3\u0026#34;\u003c/span\u003e;\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003epackage\u003c/span\u003e company;\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eimport\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Person.proto\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003emessage\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eTeam\u003c/span\u003e {\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u003c/span\u003e  \u003cspan style=\"color:#66d9ef\"\u003erepeated\u003c/span\u003e people.Employee members \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u003c/span\u003e}\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo compile our new message, we need to tell \u003ccode\u003ebuild.rs\u003c/code\u003e where it can find both the \u003ccode\u003ePerson.proto\u003c/code\u003e and the \u003ccode\u003eTeam.proto\u003c/code\u003e schema files, as well as how to reference the messages in the \u003ccode\u003epeople\u003c/code\u003e namespace.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Other awesome build stuff\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    generate_schemas();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003egenerate_schemas\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003elet\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003emut\u003c/span\u003e config \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e prost_build::Config::new();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Map the people protocol buffer namespace to the people::proto Rust module\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    config.extern_path(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;.people\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;people::proto\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    config\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        .compile_protos(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#75715e\"\u003e// Files we want to compile\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e[\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;./schema/Team.proto\u0026#34;\u003c/span\u003e],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#75715e\"\u003e// Folders where we can find our imports\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e[\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;./schema\u0026#34;\u003c/span\u003e],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        .expect(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Prost protobuf compilation error;\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe function \u003ccode\u003eextern_path\u003c/code\u003e takes two arguments, the first one is the name of the namespace within the Protocol Buffers schema, and the second argument is the namespace within Rust.\u003c/p\u003e\n\u003cp\u003eThe first argument of \u003ccode\u003ecompile_protos\u003c/code\u003e is the files that we want to compile, the second argument is the directories in which we can find our schemas.\u003c/p\u003e\n\u003cp\u003eNote that we import \u003ccode\u003ePeople.proto\u003c/code\u003e in our new Protocol Buffers schema. Currently the only way that I know to get this to work, is to manually copy the \u003ccode\u003e.proto\u003c/code\u003e file in the other package into your own application.\u003c/p\u003e\n\u003cp\u003eThere you have it! Next you can include the generated output into your Rust application. If you know of a better way to include external schemas without copying the schemas, please reach out.\u003c/p\u003e\n",
    "date_published": "2024.10.05",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/hunter-gathering-apple-picking/",
    "url": "https://brandonrozek.com/blog/hunter-gathering-apple-picking/",
    "title": "Hunter-Gatherer Traditions: Apple Picking",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOne of the fun yearly traditions that Clare and I have is to go apple picking. After all, we live in a state that produces \u003ca href=\"https://www.ers.usda.gov/data-products/chart-gallery/gallery/chart-detail/?chartId=75112\"\u003ea lot of apples\u003c/a\u003e. Apple picking season always happen earlier than we expect, with peak season happening in September and October.\u003c/p\u003e\n\u003cp\u003eWe went to \u003ca href=\"https://www.windyhillorchardnye.com\"\u003eWindy Hill Orchard\u003c/a\u003e and they have the following apples available: Cortland, Empire, Fuji, Gala, Golden Delicious, Janogold, Macoun, McIntosh, and Red Delicious.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/PXL_20240922_163519846.MP.jpg\" alt=\"Row of Apple Trees\"\u003e\u003c/p\u003e\n\u003cp\u003eFrom previous years, we decided that we like Empire apples and Fuji apples the best. We like to snack on the Empire apples, where we typically bake more with Fuji Apples.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/PXL_20240922_164955826.jpg\" alt=\"Picking a specific apple\"\u003e\u003c/p\u003e\n\u003cp\u003eDid you know that Apples are picked during peak season and preserved year-round to keep availability in grocery stores? In fact, only \u003ca href=\"PXL_20240922_164955826.jpg\"\u003e5% of apples in the US are imported\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eUnfortunately, we don\u0026rsquo;t have the equipment at home to keep apples for the entire year. That doesn\u0026rsquo;t stop us from collecting an entire bag of apples regardless and sticking it in the fridge.\u003c/p\u003e\n\u003cp\u003eNot all apples peak at the same time. Empire apples start peaking in September while Fuji apples start peaking in October. Since we went last week, this meant that sadly the Fuji apples weren\u0026rsquo;t quite ripe yet.\u003c/p\u003e\n\u003cp\u003eApple trees aren\u0026rsquo;t particularly tall. There were plenty of apples within reaching distance. However, some came extra prepared  with long poles with baskets in the end called a \u003cem\u003efruit picker\u003c/em\u003e. (\u003ca href=\"https://www.homedepot.com/p/Corona-12-ft-L-Reach-Extendable-Aluminum-Handle-Fruit-Picker-FP-2312/318284444\"\u003eExample\u003c/a\u003e)\u003c/p\u003e\n\u003cp\u003eLeave it to humans, to come prepared with tools when hunting and gathering.\u003c/p\u003e\n",
    "date_published": "2024.09.28",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/webhook-notifications-on-systemd-service-failure/",
    "url": "https://brandonrozek.com/blog/webhook-notifications-on-systemd-service-failure/",
    "title": "Webhook notifications on systemd service failure",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eEvery morning like every good system administrator, I log onto all my machines and type the following command\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemctl --failed\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis gives me a list of all my systemd services that have failed. I pray that it\u0026rsquo;s empty\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e  UNIT LOAD ACTIVE SUB DESCRIPTION\n0 loaded units listed.\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eExcept, I don\u0026rsquo;t.\u003c/p\u003e\n\u003cp\u003eInstead, I have it set up so that I receive a webhook notification via \u003ca href=\"https://zulip.com\"\u003eZulip\u003c/a\u003e whenever a service fails. With the right infrastructure in place, it\u0026rsquo;s as simple as adding a \u003ccode\u003eOnFailure\u003c/code\u003e line to all the services you want to monitor.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eStep 1:\u003c/strong\u003e Setting up the webhook.\u003c/p\u003e\n\u003cp\u003eOn Zulip, I use the \u003ca href=\"https://zulip.com/integrations/doc/slack_incoming\"\u003eSlack incoming webhook\u003c/a\u003e integration. (Note the \u003ca href=\"https://zulip.com/api/incoming-webhooks-overview#url-specification\"\u003eURL specification\u003c/a\u003e)\u003c/p\u003e\n\u003cp\u003eAs you might guess, this style of webhook works on Slack and on Discord as well.\u003c/p\u003e\n\u003cp\u003eFor our notification script we\u0026rsquo;ll need two environmental variables\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eName\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eSERVICE\u003c/td\u003e\n          \u003ctd\u003eThe name of the \u003ccode\u003esystemd\u003c/code\u003e service. We will automatically populate this\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eWEBHOOK_URL\u003c/td\u003e\n          \u003ctd\u003eThe URL to send the webhook to. This is chat application specific.\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eWe\u0026rsquo;ll need the following CLI applications installed\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eName\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003ecurl\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eSends the POST request.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003ejq\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eSanitizes the log output before sending it to curl.\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eThe script \u003ccode\u003e/bin/webhook-notify.sh\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e -z \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$SERVICE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;SERVICE variable not set or empty\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e -z \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$WEBHOOK_URL\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;WEBHOOK_URL variable not set or empty\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e ! command -v jq \u0026amp;\u0026gt; /dev/null; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;jq is not installed\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLOG_CONTENTS\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003esystemctl status --full --no-pager \u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eSERVICE\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e | jq -Rsa .\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl -X POST --data-urlencode \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;payload={\\\u0026#34;text\\\u0026#34;: \u003c/span\u003e$LOG_CONTENTS\u003cspan style=\"color:#e6db74\"\u003e}\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eWEBHOOK_URL\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eMake the script executable\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echmod u+x /bin/webhook-notify.sh\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAt this point you should be able to test out the script and make sure you get notifications. Set the two environmental variables and run the script.\u003c/p\u003e\n\u003cp\u003eExample:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport WEBHOOK_URL\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://INSERT-NAMESPCE.zulipchat.com/api/v1/external/slack_incoming?api_key=INSERT-API-KEY\u0026amp;stream=INSERT-STREAM-ID\u0026amp;topic=Systemd\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport SERVICE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eNetworkManager\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e/bin/webhook-notify.sh\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eStep 2:\u003c/strong\u003e Setup the Systemd Service\u003c/p\u003e\n\u003cp\u003eWhen a systemd unit fails, we are able to call another systemd service. The service that we\u0026rsquo;ll call will run our script from the last step.\u003c/p\u003e\n\u003cp\u003eIn \u003ccode\u003e/etc/systemd/system/webhook-notify@.service\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eSend Systemd Notifications via Webhook\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eoneshot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eEnvironment\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eWEBHOOK_URL=\u0026#34;INSERT-WEBHOOK-URL-HERE\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eEnvironment\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eSERVICE=%i\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/bin/webhook-notify.sh\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNote the \u003ccode\u003e@\u003c/code\u003e in the filename. This is important since this service will run with the failed unit name as the argument that appears after the \u003ccode\u003e@\u003c/code\u003e. Within the script, this is the \u003ccode\u003e%i\u003c/code\u003e variable.\u003c/p\u003e\n\u003cp\u003eExample test:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl start webhook-notify@NetworkManager\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eStep 3:\u003c/strong\u003e Add \u003ccode\u003eOnFailure\u003c/code\u003e to all the services we want to monitor\u003c/p\u003e\n\u003cp\u003eWithin the \u003ccode\u003e[Unit]\u003c/code\u003e section of our Systemd service, add the following\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eOnFailure\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ewebhook-notify@%i.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2024.09.04",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/writing-unit-tests-lean-4/",
    "url": "https://brandonrozek.com/blog/writing-unit-tests-lean-4/",
    "title": "Writing Unit Tests in Lean 4",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen writing Lean code, it\u0026rsquo;s easier to iterate with unit tests than to prove properties about the function off the bat.\u003c/p\u003e\n\u003cp\u003eI mean if the unit test doesn\u0026rsquo;t even pass, why bother with the proof?\u003c/p\u003e\n\u003cp\u003eLuckily, Lean 4 let\u0026rsquo;s us do unit tests with a cool command called \u003ccode\u003e#guard\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003e#guard 1 = 1\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis checks whether the following expression evaluates to \u003ccode\u003etrue\u003c/code\u003e. Note that this does not provide a proof since this check is done using the untrusted evaluator.\u003c/p\u003e\n\u003cp\u003eWhat does need to be proven, however, is that the expression given is decidable.\u003c/p\u003e\n\u003cp\u003eOne issue I faced is that I often use the \u003ccode\u003eExcept\u003c/code\u003e type for error handling in my code.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003einductive Except (ε : Type u) (α : Type v) where\n  /-- A failure value of type `ε` -/\n  | error : ε → Except ε α\n  /-- A success value of type `α` -/\n  | ok    : α → Except ε α\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFor a simple (somewhat silly) example, look at the following function\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003edef gt_0 (n : Nat) : Except String Bool :=\n  if n = 0 then .error s!\u0026#34;{n} is not greater than zero\u0026#34;\n  else .ok true\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe can evaluate our function on a given input:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003e#eval gt_0 1 -- Except.ok true\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eHowever, we can\u0026rsquo;t guard against it without encountering an error. This is because we haven\u0026rsquo;t shown that the equality is decidable.\u003c/p\u003e\n\u003cp\u003eTo help with this, I wrote up a function that we can apply generically.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003edef Except.deq [DecidableEq α] [DecidableEq β] : DecidableEq (Except α β) := by\n  unfold DecidableEq\n  intro a b\n  cases a \u0026lt;;\u0026gt; cases b \u0026lt;;\u0026gt;\n  -- Get rid of obvious cases where .ok != .err\n  try { apply isFalse ; intro h ; injection h }\n  case error.error c d =\u0026gt;\n    match decEq c d with\n      | isTrue h =\u0026gt; apply isTrue (by rw [h])\n      | isFalse _ =\u0026gt; apply isFalse (by intro h; injection h; contradiction)\n  case ok.ok c d =\u0026gt;\n    match decEq c d with\n      | isTrue h =\u0026gt; apply isTrue (by rw [h])\n      | isFalse _ =\u0026gt; apply isFalse (by intro h; injection h; contradiction)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe can then show that equality of \u003ccode\u003eExcept String Bool\u003c/code\u003e is decidable, since Lean already knows that string and boolean equality is decidable.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003einstance: DecidableEq (Except String Bool) := Except.deq\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFrom here, we can use \u003ccode\u003e#guard\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003e#guard gt_0 1 = (Except.ok true)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWhen a \u003ccode\u003e#guard\u003c/code\u003e fails, it will throw an error during compilation. This is great for ensuring that our unit tests pass during compilation.\u003c/p\u003e\n",
    "date_published": "2024.08.05",
    "tags": ["Formal Methods"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/polymorphic-functions-wildcard-matching-lean-4/",
    "url": "https://brandonrozek.com/blog/polymorphic-functions-wildcard-matching-lean-4/",
    "title": "Polymorphic Functions w/ Wildcard Matching in Lean 4",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI was reading through the \u003ca href=\"https://lean-lang.org/functional_programming_in_lean/getting-to-know/polymorphism.html\"\u003ePolymorphism\u003c/a\u003e section in the Functional programming in Lean textbook and came across the following example:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003einductive Sign where\n  | pos\n  | neg\n\ndef posOrNegThree (s : Sign) : match s with | Sign.pos =\u0026gt; Nat | Sign.neg =\u0026gt; Int :=\n  match s with\n  | Sign.pos =\u0026gt; (3 : Nat)\n  | Sign.neg =\u0026gt; (-3 : Int)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe function \u003ccode\u003eposOrNegThree\u003c/code\u003e depending on the input, can either return an expression of type \u003ccode\u003eNat\u003c/code\u003e or an expression of type \u003ccode\u003eInt\u003c/code\u003e. That\u0026rsquo;s super neat!\u003c/p\u003e\n\u003cp\u003eWhat happens if we add a wildcard to the match?\u003c/p\u003e\n\u003cp\u003eFor example, let\u0026rsquo;s say we want a type based on the number of bits of precision specified. If we don\u0026rsquo;t support the number of bits, we return the arbitrary precision \u003ccode\u003eNat\u003c/code\u003e as our default.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003edef UIntN (n: Nat) : Type := match n with\n  | 32 =\u0026gt; UInt32\n  | 64 =\u0026gt; UInt64\n  | _ =\u0026gt; Nat\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow let\u0026rsquo;s write a function that returns the zero element of our specified type:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003edef u0 (x: Nat) : UIntN x := match x with\n  | 32 =\u0026gt; (0: UInt32)\n  | 64 =\u0026gt; (0: UInt64)\n  | _ =\u0026gt; Nat.zero\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis will result in the following error:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003etype mismatch\n  Nat.zero\nhas type\n  ℕ : Type\nbut is expected to have type\n  UIntN x✝ : Type\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eI got stuck on this for a while, so I asked on the really helpful Lean Zulip and got a \u003ca href=\"https://leanprover.zulipchat.com/#narrow/stream/113489-new-members/topic/.E2.9C.94.20Polymorphic.20Functions.20w.2F.20Wildcard.20Match.20Type\"\u003egreat response\u003c/a\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003edef u0 (x: Nat) : UIntN x := dite (x=32) (λ h ↦ by\n    subst h\n    exact (0 : UInt32)\n  ) (dite (x = 64) (λ h ↦ by\n    intro h2\n    subst h\n    exact (0: UInt64)\n    )\n    (λ h ↦ by\n    intro h2\n    have : UIntN x = Nat := by\n      unfold UIntN\n      simp only\n    rw [this]\n    exact 0\n  ))\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eUnfortunately this doesn\u0026rsquo;t look pretty, but this was a massive clue in finding the prettier syntax to solve this problem!\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003edef u0 (x: Nat) : UIntN x :=\n  if h: x = 6 then by\n    subst h\n    exact (0: UInt32)\n  else if h2: x = 4 then by\n    subst h2\n    exact (0: UInt64)\n  else by\n    have : UIntN x = Nat := by\n      unfold UIntN\n      simp only\n    rw [this]\n    exact 0\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2024.08.04",
    "tags": ["Formal Methods"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/modifying-json-in-rust/",
    "url": "https://brandonrozek.com/blog/modifying-json-in-rust/",
    "title": "Modifying JSON in Rust",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eFor my new role, I\u0026rsquo;ve been learning Rust. I don\u0026rsquo;t know why I didn\u0026rsquo;t learn it earlier, I enjoy it so much that I decided to power my \u003ca href=\"/toots\"\u003etoots\u003c/a\u003e page with it.\u003c/p\u003e\n\u003cp\u003eWhen I query my toots, I receive a JSON response like the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[{\u003cspan style=\"color:#f92672\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;112581869512127927\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e...,\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026#34;content\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;...\u0026#34;\u003c/span\u003e,}, \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e...\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSome parts of the JSON response changes quite frequently. Since I \u003ca href=\"https://brandonrozek.com/blog/archiving-toots/\"\u003earchive\u003c/a\u003e the changes, I want to strip out the highly dynamic information before saving it off to a file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003elet\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003emut\u003c/span\u003e json_response: \u003cspan style=\"color:#a6e22e\"\u003eserde_json\u003c/span\u003e::Value \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e serde_json::from_str(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;...\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t.expect(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;JSON parse error\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn order to modify this variable, we need to have some knowledge of it\u0026rsquo;s structure. I\u0026rsquo;ll show in this post how to modify our JSON data given whether we\u0026rsquo;re working with a JSON array or a JSON object.\u003c/p\u003e\n\u003ch2 id=\"serde_jsonvaluearray\"\u003e\u003ccode\u003eserde_json::Value::Array\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eOur example JSON starts off as an array, so let\u0026rsquo;s extract that out:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003elet\u003c/span\u003e json_array \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e json_response\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t.as_array_mut()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t.expect(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Expected JSON Array\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003eas_array_mut\u003c/code\u003e says to interpret the \u003ccode\u003ejson_response\u003c/code\u003e variable as an array.  The \u003ccode\u003emut\u003c/code\u003e component is important for us to be able to edit the data in place without making copies.\u003c/p\u003e\n\u003cp\u003eThe \u003ccode\u003eas_array_mut\u003c/code\u003e method returns an option type. Calling \u003ccode\u003e.expect(...)\u003c/code\u003e on it will cause the program to crash if it isn\u0026rsquo;t indeed an array. We can alternatively perform some error handling:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003elet\u003c/span\u003e Some(json_array) \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e json_response.as_array_mut() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Do something with json_array\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e} \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Error handling here\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThough I\u0026rsquo;ll assume that you\u0026rsquo;re following best practices and not discuss more about error handling in this post.\u003c/p\u003e\n\u003cp\u003eOur variable \u003ccode\u003ejson_array\u003c/code\u003e has type \u003ccode\u003e\u0026amp;mut Vec\u0026lt;serde_json::Value\u0026gt;\u003c/code\u003ewhich means we can do things like add another element to said array.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003elet\u003c/span\u003e new_element \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e serde_json::Value::from(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ejson_array.push(new_element);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can also remove the last element of the array if it exists\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ejson_array.pop()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"serde_jsonvalueobject\"\u003e\u003ccode\u003eserde_json::Value::Object\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eWithin the array, we have a list of objects. Let us grab the first element as an example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003elet\u003c/span\u003e first_item \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e json_array.get_mut(\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e).unwrap();\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn order to be able to modify the data, we use the \u003ccode\u003eget_mut\u003c/code\u003e method. This, like before, returns an option if it doesn\u0026rsquo;t exist. We can call \u003ccode\u003eunwrap\u003c/code\u003e on it to get access to the data or panic if the element doesn\u0026rsquo;t exist.\u003c/p\u003e\n\u003cp\u003eThe variable \u003ccode\u003efirst_item\u003c/code\u003e has type \u003ccode\u003eserde_json::Value\u003c/code\u003e. To interpret this as an object, we need to call \u003ccode\u003eas_object_mut\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003elet\u003c/span\u003e first_item_obj \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e first_item.as_object_mut().unwrap();\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow our variable \u003ccode\u003efirst_item_obj\u003c/code\u003e has type \u003ccode\u003e\u0026amp;mut Map\u0026lt;String, serde_json::Value\u0026gt;\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eWe can remove any fields that we don\u0026rsquo;t think is important\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efirst_item_obj.remove(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;bot\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAdd any fields we want\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003elet\u003c/span\u003e new_key \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;PoweredBy\u0026#34;\u003c/span\u003e.to_string();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003elet\u003c/span\u003e new_value \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e serde_json::Value::from(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Rust\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etoot.insert(new_key, new_value);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRenaming a field is the combination of the last two:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003elet\u003c/span\u003e toot_date \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e toot.remove(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;created_at\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t.expect(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Missing created_at\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etoot.insert(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;date\u0026#34;\u003c/span\u003e.to_string(), toot_date);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2024.08.03",
    "tags": ["Rust"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/accessing-homelab-services-mtls/",
    "url": "https://brandonrozek.com/blog/accessing-homelab-services-mtls/",
    "title": "Accessing Homelab Services Remotely with mTLS",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOne common approach to securely accessing remote services is to use a VPN. This builds an encrypted networking layer which makes it easy to use SSH, HTTPS, and other various protocols.\u003c/p\u003e\n\u003cp\u003ePersonally, this worked great on my Linux machines. However, I found it a pain to use on a mobile device.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eOn Android, you can only use one VPN at any given time. This is the case even when the IP ranges are not in conflict.\u003c/li\u003e\n\u003cli\u003eIt\u0026rsquo;s difficult to edit DNS host entries. Especially without root, or generating another VPN profile.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eOn my phone, I want to be able to open my browser and go to a publicly resolvable URL to access my homelab services in a secure way. What\u0026rsquo;s very common in enterprise settings is to run an authenticated proxy through a single sign-on (SSO) service. This would normally prompt for a username and password, followed sometimes with a 2FA code before redirecting you to the service you want.\u003c/p\u003e\n\u003cp\u003eI don\u0026rsquo;t fully trust myself to securely maintain a SSO service as a hobby. Also instead of typing in a username and password each time, I want to rely on keys instead. This is where mutual-TLS (mTLS) comes in.\u003c/p\u003e\n\u003cp\u003eIn the standard TLS setup, the server provides the client with its public key so that the client can send encrypted communications as well as validate that the server is who they say they are. This validation is done through trusted certificate authorities.\u003c/p\u003e\n\u003cp\u003eThe \u003ca href=\"https://cabforum.org/\"\u003eCA/Browser Forum\u003c/a\u003e is a self-regulated group that issues a \u003ca href=\"https://cabforum.org/working-groups/netsec/documents/\"\u003eset of requirements\u003c/a\u003e for certificate authorities to follow. Ultimately, vendors have their own policies (ex: \u003ca href=\"https://www.mozilla.org/en-US/about/governance/policies/security-group/certs/policy/\"\u003eMozilla\u003c/a\u003e) for deciding whether to trust a SSL certificate coming from a certificate authority. However, many follow the CAB forum guidelines.\u003c/p\u003e\n\u003cp\u003eWhen a server is configured with mTLS, not only does the client authenticate the server but the server also authenticates the client. If the client doesn\u0026rsquo;t respond with the proper certificate, then the server will return a HTTP 400 error.\u003c/p\u003e\n\u003ch3 id=\"overall-setup\"\u003eOverall Setup\u003c/h3\u003e\n\u003cp\u003eTo setup an authenticated proxy with mTLS, we will use \u003ccode\u003enginx\u003c/code\u003e as the reverse proxy and Cloudflare\u0026rsquo;s PKI and TLS toolkit \u003ca href=\"https://github.com/cloudflare/cfssl\"\u003e\u003ccode\u003ecfssl\u003c/code\u003e\u003c/a\u003e. You can replace the \u003ccode\u003ecfssl\u003c/code\u003e commands with the equivalent \u003ccode\u003eopenssl\u003c/code\u003e ones, but I won\u0026rsquo;t cover that in this post.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ll have a publicly available server with the reverse proxy installed and setup to relay traffic over the VPN to my homelab. We\u0026rsquo;ll use certificates generated by \u003ccode\u003ecfssl\u003c/code\u003e to authenticate the client and server.\u003c/p\u003e\n\u003cp\u003eI also wrote about \u003ccode\u003ecfssl\u003c/code\u003e \u003ca href=\"https://brandonrozek.com/blog/internalca/\"\u003ein the past\u003c/a\u003e, but I\u0026rsquo;ll keep this blog post self-contained.\u003c/p\u003e\n\u003ch3 id=\"setting-up-the-certificates\"\u003eSetting up the certificates\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003ecfssl\u003c/code\u003e relies on having JSON files for its configuration. First let\u0026rsquo;s set up the certificate authority in a file called \u003ccode\u003ecsr_ca.json\u003c/code\u003e replacing the fields as needed:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;CN\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;SomeCommonName\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;key\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;algo\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;rsa\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;size\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e4096\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;names\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;C\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;US\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;O\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;SomeOrg\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;OU\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;SomeOrgUnit\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;ST\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;New York\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;L\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;New York\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen generate the root certificates for our certificate authority\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecfssl gencert -initca csr_ca.json | cfssljson -bare ca\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will generate the following files\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eFilename\u003c/th\u003e\n          \u003cth\u003ePurpose\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eca.pem\u003c/td\u003e\n          \u003ctd\u003ePublic Certificate\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eca-key.pem\u003c/td\u003e\n          \u003ctd\u003ePrivate Key\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eca.csr\u003c/td\u003e\n          \u003ctd\u003eCertificate Signing Request\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eWith these files that constitute our certificate authority, we\u0026rsquo;ll generate the server and client certificates.\u003c/p\u003e\n\u003cp\u003eWhen generating the server certificate, we need to declare which URLs the certificate is valid for. Instead of creating a new certificate for every service in my homelab, we\u0026rsquo;ll make use of a wildcard certificate on a particular domain.\u003c/p\u003e\n\u003cp\u003eAs a personal preference, I like to keep my certificates separated by device. Create a new folder called \u003ccode\u003eserver\u003c/code\u003e with the following in \u003ccode\u003ecsr_server.json\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;hosts\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;*.internal.somedomain.com\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;key\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;algo\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;rsa\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;size\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e4096\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;names\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;C\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;US\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;O\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;SomeOrg\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;OU\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;SomeOrgUnit\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;ST\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;New York\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;L\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;New York\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen we similarly create three certificates relating to the server with the following command\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecfssl gencert -ca\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e../ca.pem -ca-key\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e../ca-key.pem csr_server.json | cfssljson -bare cert\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHere I assumed the root certificates are stored one level up (\u003ccode\u003e..\u003c/code\u003e) but replace the path as necessary.\u003c/p\u003e\n\u003cp\u003eTo match how \u003ccode\u003enginx\u003c/code\u003e expects its certificates, we\u0026rsquo;ll perform some renames and concatenations.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emv cert-key.pem privkey.pem\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emv cert.pem chain.pem\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecat privkey.pem \u0026gt; fullchain.pem\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecat chain.pem \u0026gt;\u0026gt; fullchain.pem\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe\u0026rsquo;ll need to run \u003ccode\u003ecfssl\u003c/code\u003e one more time to generate our client\u0026rsquo;s certificates. Create a folder for the specific client and have the following with \u003ccode\u003ecsr_client.json\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;key\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;algo\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;rsa\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;size\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e4096\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;names\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;C\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;US\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;O\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;SomeOrg\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;OU\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;SomeOrgUnit\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;ST\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;New York\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;L\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;New York\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen generate the certificates:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecfssl gencert -ca\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e../ca.pem -ca-key\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e../ca-key.pem csr_client.json | cfssljson -bare cert\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFirefox expects the key in a specific format, so we\u0026rsquo;ll ask \u003ccode\u003eopenssl\u003c/code\u003e to create it for us.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eopenssl pkcs12 -export -out user.pfx -inkey cert-key.pem -in cert.pem -certfile ../ca.pem\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhen running this command, it\u0026rsquo;ll ask for an export password. You\u0026rsquo;ll need to remember this when attempting to import this key. You can leave it blank if you\u0026rsquo;re intending to use it for Firefox, but I found that Android devices won\u0026rsquo;t accept this unless some password is set.\u003c/p\u003e\n\u003cp\u003eBefore we can import \u003ccode\u003euser.pfx\u003c/code\u003e on our favorite device, we need to have our device trust the root certificate authority. Since we created our own and didn\u0026rsquo;t go through a CA like Let\u0026rsquo;s Encrypt, devices will not trust the certificates by default.\u003c/p\u003e\n\u003cp\u003eOn Fedora, copy \u003ccode\u003eca.pem\u003c/code\u003e to \u003ccode\u003e/etc/pki/ca-trust/source/anchors/\u003c/code\u003e and then run \u003ccode\u003esudo update-ca-trust\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eOn Android you can import \u003ccode\u003eca.pem\u003c/code\u003e via \u003ccode\u003eMore security \u0026amp; privacy \u0026gt; Encryption \u0026amp; credentials -\u0026gt; Install a certificate -\u0026gt; CA certificate\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eAfter install the CA public certificate, we can install \u003ccode\u003euser.pfx\u003c/code\u003e via \u003ccode\u003eMore security \u0026amp; privacy \u0026gt; Encryption \u0026amp; credentials -\u0026gt; Install a certificate -\u0026gt; VPN \u0026amp; app user certificate\u003c/code\u003e. On Fedora, you can do this within Firefox.\u003c/p\u003e\n\u003ch3 id=\"nginx-authenticated-proxy-setup\"\u003eNginx Authenticated Proxy Setup\u003c/h3\u003e\n\u003cp\u003eSince we\u0026rsquo;re looking to access multiple homelab services from our authenticated proxy, we\u0026rsquo;ll make use of regexes in the \u003ccode\u003eserver_name\u003c/code\u003e so that we only need to write one config.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eserver_name\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;~^(?\u0026lt;subdomain\u0026gt;.+)\\.internal\\.somedomain\\.com\u003c/span\u003e$\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis matches on \u003ccode\u003e*.internal.somedomain.com\u003c/code\u003e where the \u003ccode\u003e*\u003c/code\u003e wildcard means that it can be any string. The regex looks slightly different since we want to capture the wildcard portion in a variable called \u003ccode\u003e$subdomain\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eA good security posture is to provide a white-list of what subdomains to allow.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eset\u003c/span\u003e $valid_subdomain \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e(\u003c/span\u003e$subdomain = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;X\u0026#34;)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eset\u003c/span\u003e $valid_subdomain \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e(\u003c/span\u003e$subdomain = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Y\u0026#34;)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eset\u003c/span\u003e $valid_subdomain \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e(\u003c/span\u003e$valid_subdomain = \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e403\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can see here that if we don\u0026rsquo;t match on \u003ccode\u003eX.internal.somedomain.com\u003c/code\u003e or \u003ccode\u003eY.internal.somedomain.com\u003c/code\u003e that we will return a \u003ccode\u003e403 Not Authorized\u003c/code\u003e HTTP code.\u003c/p\u003e\n\u003cp\u003eTo enable mTLS, we need to turn on client verification. For this, we need the certificate of the root CA.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003essl_verify_client\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eon\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003essl_client_certificate\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/path/to/root/certificates/ca.pem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen when we do the proxying, we need to set the \u003ccode\u003eHost\u003c/code\u003e variable so that it does not have the \u003ccode\u003einternal\u003c/code\u003e component of the domain name.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003elocation\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eproxy_pass\u003c/span\u003e  \u003cspan style=\"color:#e6db74\"\u003ehttps://10.10.10.2\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eHost\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$subdomain.somedomain.com\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e//\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eOther\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eoptions\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eomitted\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003efor\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ebrevity\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe proxy URL shouldn\u0026rsquo;t allow outside traffic by default. Otherwise people can bypass the authentication proxy! You can do this by either (1) only responding when the source IP matches the proxy IP, or (2) having the URL only resolvable when using a VPN.\u003c/p\u003e\n\u003cp\u003eCheck the bottom of this post for the whole config, but when that\u0026rsquo;s all setup we can restart the \u003ccode\u003enginx\u003c/code\u003e service and verify that no errors are shown.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl restart nginx\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWith that, we should be able to securely access our homelab remotely! Give it a test, visiting \u003ccode\u003eX.internal.somedomain.com\u003c/code\u003e replacing the relevant parts of the URL. Your browser should then prompt for the certificate, and if all goes well you should see the service.\u003c/p\u003e\n\u003cp\u003eOn Android, sadly the Firefox app doesn\u0026rsquo;t support mTLS, but the default Chrome browser does. On desktop, both Firefox and Chrome does support mTLS.\u003c/p\u003e\n\u003cp\u003eFeel free to write in if you have any questions about your setup.\u003c/p\u003e\n\u003cp\u003eFull Nginx config which I store at \u003ccode\u003e/etc/nginx/conf.d/internal.conf\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003emap\u003c/span\u003e $http_upgrade $connection_upgrade {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003edefault\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eupgrade\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#39;\u0026#39;\u003c/span\u003e      \u003cspan style=\"color:#e6db74\"\u003eclose\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eserver\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e[::]:80\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eserver_name\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;~^(?\u0026lt;subdomain\u0026gt;.+)\\.internal\\.somedomain\\.com\u003c/span\u003e$\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elocation\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e301\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ehttps://\u003c/span\u003e$host$request_uri;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eserver\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e443\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003essl\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e[::]:443\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003essl\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ehttp2\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eon\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eserver_name\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;~^(?\u0026lt;subdomain\u0026gt;.+)\\.internal\\.somedomain\\.com\u003c/span\u003e$\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eset\u003c/span\u003e $valid_subdomain \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e(\u003c/span\u003e$subdomain = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;X\u0026#34;)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eset\u003c/span\u003e $valid_subdomain \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e(\u003c/span\u003e$valid_subdomain = \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e403\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003essl_certificate\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/path/to/server/certificates/fullchain.pem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003essl_certificate_key\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/path/to/server/certificates/privkey.pem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003einclude\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/etc/letsencrypt/conf/options-ssl-nginx.conf\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003essl_dhparam\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/etc/letsencrypt/conf/ssl-dhparams.pem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003essl_trusted_certificate\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/path/to/server/certificates/chain.pem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003essl_stapling\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eon\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003essl_stapling_verify\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eon\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003essl_verify_client\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eon\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003essl_client_certificate\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/path/to/root/certificates/ca.pem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elocation\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_pass\u003c/span\u003e  \u003cspan style=\"color:#e6db74\"\u003ehttps://10.10.10.2\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eHost\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$subdomain.somedomain.com\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eX-Real-IP\u003c/span\u003e $remote_addr;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eX-Forwarded-For\u003c/span\u003e $proxy_add_x_forwarded_for;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eX-Forwarded-Host\u003c/span\u003e $server_name;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eX-Forwarded-Proto\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ehttps\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eUpgrade\u003c/span\u003e $http_upgrade;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eConnection\u003c/span\u003e $connection_upgrade;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_cache_bypass\u003c/span\u003e $http_upgrade;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2024.07.21",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/re-static-plus/",
    "url": "https://brandonrozek.com/blog/re-static-plus/",
    "title": "Re: Static+, can it work?",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eGabriel recently released a \u003ca href=\"https://gabe.rocks/tech/static-plus/\"\u003eblog post\u003c/a\u003e thinking about the space between a static website and a full-blown CMS.\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s something that I think about semi-often. It\u0026rsquo;s difficult to characterize what \u003ccode\u003estatic+\u003c/code\u003e means, because I believe we each have our reasons for using a static site generator. This includes but is not limited to \u003ca href=\"https://stackoverflow.com/questions/6869210/which-is-faster-mysql-php-or-serving-straight-from-static-files\"\u003espeed\u003c/a\u003e, \u003ca href=\"https://nvd.nist.gov/vuln/search/results?form_type=Basic\u0026amp;results_type=overview\u0026amp;query=wordpress\u0026amp;search_type=all\"\u003esecurity\u003c/a\u003e, and my personal reason of \u003ca href=\"https://en.wikipedia.org/wiki/Data_portability\"\u003edata portability\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eI like how my Hugo website is written in Markdown files. This means that if Hugo stopped development now, I can move to a different static site generator in the future.\u003c/p\u003e\n\u003cp\u003eWith this in mind, what does \u003ccode\u003estatic+\u003c/code\u003e mean for me? For data portability, this means that if I want an API to create a blog post ideally it\u0026rsquo;ll:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eWrite the contents of the blog post in the API request to a Markdown file\u003c/li\u003e\n\u003cli\u003eCommit it to my git repository\u003c/li\u003e\n\u003cli\u003eRe-run \u003ccode\u003ehugo\u003c/code\u003e to build the site\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eI enjoy my writing workflow on my computer, so the idea is crafting some easy way to compose posts on my phone.\u003c/p\u003e\n\u003cp\u003eHugo maintains a \u003ca href=\"https://gohugo.io/tools/front-ends/\"\u003elist of frontends\u003c/a\u003e which I haven\u0026rsquo;t vetted but may actually satisfy those requirements. Perhaps it\u0026rsquo;s time and energy that\u0026rsquo;s preventing me from \u003ca href=\"https://jamstack.org/\"\u003eJamstack\u0026rsquo;n\u003c/a\u003e my website.\u003c/p\u003e\n\u003cp\u003eOr it could be simplicity that makes me want a simpler solution like a custom API that Gabriel describes.\u003c/p\u003e\n\u003cp\u003eOr I want a reason to tinker with a custom API :)\u003c/p\u003e\n",
    "date_published": "2024.07.01",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/beamer-gen-slide-at-section/",
    "url": "https://brandonrozek.com/blog/beamer-gen-slide-at-section/",
    "title": "Quick Beamer: Generating a slide at the beginning of each section",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003ePeople want to know where they are within your presentation. This is why for a class of long presentations, there\u0026rsquo;s an outline slide in the beginning and section headers.\u003c/p\u003e\n\u003cp\u003eSeparating sections with an additional slide is nice because it not only gives an audience an opportunity for questions, but it also allows you to sneak in a sip of water.\u003c/p\u003e\n\u003cp\u003eTo accomplish this automatically, thanks to a tip from \u003ca href=\"https://jamesoswald.dev\"\u003eJames Oswald\u003c/a\u003e, we add the following to the preamble of our Beamer file (before the \u003ccode\u003e\\begin{document}\u003c/code\u003e)\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\AtBeginSection\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[]\u003c/span\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{frame}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003e\\vfill\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003e\\textbf\u003c/span\u003e{\u003cspan style=\"color:#66d9ef\"\u003e\\insertsectionhead\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003e\\vfill\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{frame}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis produces the following slide right after your \u003ccode\u003e\\section{}\u003c/code\u003e code:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20240615193803.png\" alt=\"Image of beamer slide with the section text in bold\"\u003e\u003c/p\u003e\n\u003cp\u003eWhich for me and my template is equivalent to if I typed this in the beginning of each section:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{frame}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e\\textbf\u003c/span\u003e{First Awesome Section}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{frame}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSaving a little bit of typing is always nice :)\u003c/p\u003e\n\u003cp\u003eMy slide is fairly bare-bones, another cool approach \u003ca href=\"https://statisticaloddsandends.wordpress.com/2019/02/18/beamer-inserting-section-slides-before-each-section/\"\u003eshows the entire table of contents with every other section faded out.\u003c/a\u003e\u003c/p\u003e\n",
    "date_published": "2024.06.15",
    "tags": ["LaTex"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/recently-2024-06-15/",
    "url": "https://brandonrozek.com/blog/recently-2024-06-15/",
    "title": "Recently (June 2024)",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cstrong\u003eProfessional:\u003c/strong\u003e I\u0026rsquo;m well into the PhD program at this point. I\u0026rsquo;ve been fortunate enough to receive the NDSEG fellowship which funds my work, as well as many wonderful collaborators.\u003c/p\u003e\n\u003cp\u003eThis summer, I\u0026rsquo;m an applied scientist intern at Amazon Web Services. I work on the \u003ca href=\"https://www.cedarpolicy.com/\"\u003eCedar project\u003c/a\u003e which is under Automated Reasoning in Identities. I\u0026rsquo;m especially excited at the oppurtunity to write Lean code.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePersonal Life:\u003c/strong\u003e For the internship, I moved out to San Jose, California. The biggest surprise to me is just how blue the sky is almost every day!\u003c/p\u003e\n\u003cp\u003eThere\u0026rsquo;s a lot to explore here. I\u0026rsquo;m making edits on \u003ca href=\"https://www.openstreetmap.org/user/Brandon%20Rozek/\"\u003eOpenStreetMap\u003c/a\u003e using the Street Complete application on Android along the way.\u003c/p\u003e\n",
    "date_published": "2024.06.15",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/safety-constraints-pddl/",
    "url": "https://brandonrozek.com/blog/safety-constraints-pddl/",
    "title": "Constraints and Safety in PDDL",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eSometimes the end goal is not what only matters but the journey along the way. If I was able to drive to my destination safely, but I scrapped three cars along the way, then the drive isn\u0026rsquo;t successful.\u003c/p\u003e\n\u003cp\u003eIn this post, we\u0026rsquo;ll go over how to encode constraints and safety into an automated planning domain. As they\u0026rsquo;re inherently similar, we\u0026rsquo;ll look at it in the lens of constraints for the rest of the post.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eConstraints are goals that must be satisfied in every state along the plan.\u003c/li\u003e\n\u003cli\u003eSafety are invalid partial states that must be avoided along every state in the plan.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eRunning Example:\u003c/strong\u003e Suppose we\u0026rsquo;re in a three room domain laid out in the following way:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ekitchen \u0026lt;-\u0026gt; Living Room \u0026lt;-\u0026gt; Dining Room\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eYou\u0026rsquo;re currently in the kitchen holding dinner for tonight. The goal is to get dinner ready. You first need to set the table with the plates, and you need two hands for that. You can walk between the three rooms, but it\u0026rsquo;s slow and incurs extra action cost. To get around faster, you can dash. However, dashing when holding food leads to spilling some of it and making a mess. You can cleanup the mess in any room you\u0026rsquo;re in.\u003c/p\u003e\n\u003cp\u003eThe PDDL domain will look like the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-pddl\" data-lang=\"pddl\"\u003e(define (domain dinner)\n    (:requirements :typing)\n    (:types location - object)\n\n    (:predicates \n        (at ?1 - location) (CONNECTED ?l1 ?l2 - location) \n        (table-set) (dinner-ready) (mess-made ?l - location)\n        (holding-food) (at-food ?l - location)\n    )\n\n    (:action walk\n        :parameters (?l1 ?l2 - location)\n        :precondition (and \n            (at ?l1) \n            (CONNECTED ?l1 ?l2)\n        )\n        :effect (and \n            (at ?l2)\n            (not (at ?l1))\n            (increase (total-cost) 10)\n        )\n    )\n\n    (:action dash\n        :parameters (?l1 ?l2 - location)\n        :precondition (and \n            (at ?l1) \n            (CONNECTED ?l1 ?l2)\n        )\n        :effect (and \n            (at ?l2)\n            (not (at ?l1))\n            (when (holding-food) (mess-made ?l2))\n        )\n    )\n\n    (:action set-table\n        :precondition (and \n            (at dining-room) \n            (not (holding-food))\n        )\n        :effect (table-set)\n    )\n\n    (:action present-dinner\n        :precondition (and \n            (at dining-room)\n            (holding-food)\n            (table-set)\n        )\n        :effect (dinner-ready)\n    )\n\n    (:action drop\n        :parameters (?l1 - location)\n        :precondition (and \n            (holding-food)\n            (at ?l1)\n        )\n        :effect (and\n            (not (holding-food))\n            (at-food ?l1)\n        )\n    )\n\n    (:action pickup\n        :parameters (?l1 - location)\n        :precondition (and \n            (not (holding-food))\n            (at ?l1)\n            (at-food ?l1)\n        )\n        :effect (and \n            (holding-food)\n            (not (at-food ?l1))\n        )\n    )\n\n    (:action cleanup\n        :parameters (?l - location)\n        :precondition (at ?l)\n        :effect (not (mess-made ?l))\n    )\n\n)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFor the goal of having dinner ready, the problem file is the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-pddl\" data-lang=\"pddl\"\u003e(define (problem dinner)\n    (:domain dinner)\n    (:requirements :typing)\n    (:objects\n        kitchen living-room dining-room - location\n    )\n    (:init \n    \t(CONNECTED kitchen living-room)\n    \t(CONNECTED living-room kitchen)\n    \t(CONNECTED living-room dining-room)\n    \t(CONNECTED dining-room living-room)\n        (at kitchen)\n        (holding-food)\n    )\n    (:goal (dinner-ready))\n    (:metric minimize (total-cost))\n) \n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"avoiding-messes-with-constraints\"\u003eAvoiding messes with Constraints\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s say that we don\u0026rsquo;t want any messes to be made. One way to go about this is encoding this as a goal.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-pddl\" data-lang=\"pddl\"\u003e(:goal (and\n  (dinner-ready)\n  (forall (?l - location) (not (mess-made ?l)))\n))\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eLet\u0026rsquo;s ask \u003ca href=\"https://www.fast-downward.org/\"\u003eFast Downward\u003c/a\u003e to find an optimal plan.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efast-downward.sif domain.pddl problem.pddl --search \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;astar(blind())\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIt produces the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edash kitchen living-room\ncleanup living-room\ndash living-room dining-room\ncleanup dining-room\ndrop dining-room\nset-table\npickup dining-room\npresent-dinner\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNote that it still made several messes, but it cleaned up after itself to satisfy the goal. If we want to avoid making messes at all during the plan, we\u0026rsquo;ll have to use a constraint. We can add the following to the problem file:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-pddl\" data-lang=\"pddl\"\u003e(:goal (dinner-ready))\n(:constraints (and\n    (forall (?l - location) (not (mess-made ?l)))\n))\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNot all planners support this PDDL 3.0 feature. Fast Downward \u003ca href=\"https://www.fast-downward.org/PddlSupport\"\u003edoes not\u003c/a\u003e. In this case, we\u0026rsquo;ll need to do a \u003cem\u003ecompilation trick\u003c/em\u003e.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eAdd an additional predicate called \u003ccode\u003echeck\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eAdd to to the precondition of every action \u003ccode\u003e(not check)\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eAdd to the effect of every action \u003ccode\u003e(check)\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eCreate a new action that removes \u003ccode\u003echeck\u003c/code\u003e in the effect if the constraint is satisfied.\u003c/li\u003e\n\u003cli\u003eAdd \u003ccode\u003e(not (check))\u003c/code\u003e to the goal.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eFor example, our new action for making sure that messes aren\u0026rsquo;t made is the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-pddl\" data-lang=\"pddl\"\u003e(:action check-constraint\n    :precondition (and \n        (check)\n        (forall (?l - location) (not (mess-made ?l)))\n    )\n    :effect ((not (check)))\n)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eOur new domain file needs to modify every prior action:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-pddl\" data-lang=\"pddl\"\u003e(define (domain dinner)\n    (:requirements :typing)\n    (:types location - object)\n\n    (:predicates \n        (at ?1 - location) (CONNECTED ?l1 ?l2 - location) \n        (table-set) (dinner-ready) (mess-made ?l - location)\n        (holding-food) (at-food ?l - location) (check)\n    )\n\n    (:action walk\n        :parameters (?l1 ?l2 - location)\n        :precondition (and \n            (at ?l1) \n            (CONNECTED ?l1 ?l2)\n            (not (check))\n        )\n        :effect (and \n            (at ?l2)\n            (not (at ?l1))\n            (check)\n            (increase (total-cost) 10)\n        )\n    )\n\n    (:action dash\n        :parameters (?l1 ?l2 - location)\n        :precondition (and \n            (at ?l1) \n            (CONNECTED ?l1 ?l2)\n            (not (check))\n        )\n        :effect (and \n            (at ?l2)\n            (not (at ?l1))\n            (when (holding-food) (mess-made ?l2))\n            (check)\n        )\n    )\n\n    (:action set-table\n        :precondition (and \n            (at dining-room) \n            (not (holding-food))\n            (not (check))\n        )\n        :effect (and (table-set) (check))\n    )\n\n    (:action present-dinner\n        :precondition (and \n            (at dining-room)\n            (holding-food)\n            (table-set)\n            (not (check))\n        )\n        :effect (and (dinner-ready) (check))\n    )\n\n    (:action drop\n        :parameters (?l1 - location)\n        :precondition (and \n            (holding-food)\n            (at ?l1)\n            (not (check))\n        )\n        :effect (and\n            (not (holding-food))\n            (at-food ?l1)\n            (check)\n        )\n    )\n\n    (:action pickup\n        :parameters (?l1 - location)\n        :precondition (and \n            (not (holding-food))\n            (at ?l1)\n            (at-food ?l1)\n            (not (check))\n        )\n        :effect (and \n            (holding-food)\n            (not (at-food ?l1))\n            (check)\n        )\n    )\n\n    (:action cleanup\n        :parameters (?l - location)\n        :precondition (and (at ?l) (not (check)))\n        :effect (and (not (mess-made ?l)) (check))\n    )\n\n    (:action check-constraint\n        :precondition (and \n            (check)\n            (forall (?l - location) (not (mess-made ?l)))\n        )\n        :effect (not (check))\n    )\n\n)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNew Problem File:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-pddl\" data-lang=\"pddl\"\u003e(define (problem dinner)\n    (:domain dinner)\n    (:requirements :typing )\n    (:objects\n        kitchen living-room dining-room - location\n    )\n    (:init \n    \t(CONNECTED kitchen living-room)\n    \t(CONNECTED living-room kitchen)\n    \t(CONNECTED living-room dining-room)\n    \t(CONNECTED dining-room living-room)\n        (at kitchen)\n        (holding-food)\n    )\n    (:goal (and\n     (dinner-ready)\n     (not (check))\n    ))\n) \n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFast Downward will find us a plan satisfying the constraint, but with a lot of \u003ccode\u003echeck-constraint\u003c/code\u003e actions.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-pddl\" data-lang=\"pddl\"\u003edrop kitchen\ncheck-constraint\ndash kitchen living-room\ncheck-constraint\ndash living-room dining-room\ncheck-constraint\nset-table\ncheck-constraint\ndash dining-room living-room\ncheck-constraint\ndash living-room kitchen\ncheck-constraint\npickup kitchen\ncheck-constraint\nwalk kitchen living-room\ncheck-constraint\nwalk living-room dining-room\ncheck-constraint\npresent-dinner\ncheck-constraint\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"allowing-some-messes\"\u003eAllowing some messes\u003c/h2\u003e\n\u003cp\u003ePerhaps we want to introduce some mercy and instead use a \u003cem\u003estrike system\u003c/em\u003e. This would allow an agent to break a constraint at most $X$ times before saying they failed at a task.\u003c/p\u003e\n\u003cp\u003eFor this example, let us use the two-strike system. That is once they make the second mess, it is considered that the agent has failed at the task.\u003c/p\u003e\n\u003cp\u003eWe adjust the \u003ccode\u003echeck-constraint\u003c/code\u003e action from before to capture this new system:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-pddl\" data-lang=\"pddl\"\u003e(:action check-constraint\n    :precondition (check)\n    :effect (and \n        ; We haven\u0026#39;t striked out and satisfy the constraint\n        (when\n            (and \n                (forall (?l - location) (not (mess-made ?l)))\n                (not (strike-max)) \n            )\n            (not (check))\n        )\n        ; First time breaking the constraint\n        (when \n            (and\n                (not (forall (?l - location) (not (mess-made ?l))))\n                (not (strike-one))\n            )\n            (and \n                (strike-one)\n                (not (check))\n            )\n        )\n        ; Second time breaking the constraint\n        (when \n            (and\n                (not (forall (?l - location) (not (mess-made ?l))))\n                (strike-one)\n            )\n            (strike-max)\n        )             \n    )\n)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNotice that each of these conditional effects are mutually exclusive from each other. We can use a different encoding, but having only one conditional effect fire after each execution makes it easier to reason about.\u003c/p\u003e\n\u003cp\u003eThe problem file stays the same as the last example, and asking Fast Downward for an optimal plan results in the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edash kitchen living-room\ncheck-constraint\ncleanup living-room\ncheck-constraint\ndrop living-room\ncheck-constraint\ndash living-room dining-room\ncheck-constraint\nset-table\ncheck-constraint\ndash dining-room living-room\ncheck-constraint\npickup living-room\ncheck-constraint\nwalk living-room dining-room\ncheck-constraint\npresent-dinner\ncheck-constraint\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe main difference between this plan and the last one is that it makes use of the strike system to first dash from the kitchen to the living room, creating a mess. Then it avoids messes for the rest of the plan.\u003c/p\u003e\n",
    "date_published": "2024.06.09",
    "tags": ["Automated Planning"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/patch-files/",
    "url": "https://brandonrozek.com/blog/patch-files/",
    "title": "Creating Patch Files",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eDistributing software can be difficult. Is the shared library called \u003ccode\u003ex\u003c/code\u003e on this distribution or is it called \u003ccode\u003ey\u003c/code\u003e? This may lead to a lot of distribution specific code.\u003c/p\u003e\n\u003cp\u003eIt might not make sense to commit these changes to the main software repository. An alternative is to create \u003cem\u003epatch files\u003c/em\u003e. These are files that capture exactly how you want to update a given file.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s take the following C code as an example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;stdio.h\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Goodbye World\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\n\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eInstead, we want for it to print out the classic \u0026ldquo;Hello World\u0026rdquo;. To do this, first let us save the original file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecp hello-world.c hello-world.c.orig\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen let\u0026rsquo;s edit \u003ccode\u003ehello-world.c\u003c/code\u003e to produce the desired result\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;stdio.h\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Hello World\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\n\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow we can create the patch file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ediff -Naur hello-world.c.orig hello-world.c \u0026gt; hello.patch\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe options of \u003ccode\u003ediff\u003c/code\u003e are the following:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eFlag\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-N\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eTreat absent files as empty\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-a\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eTreat all files as text\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-u\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eOutput 3 lines of unified context\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-r\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eRecursively compare any subdirectories found\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eThis will output the following patch file\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-diff\" data-lang=\"diff\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e--- hello-world.c.orig  2024-05-12 20:41:27.708297085 -0400\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e+++ hello-world.c       2024-05-12 20:41:36.742348955 -0400\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e@@ -2,7 +2,7 @@\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e int main(void)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e-  printf(\u0026#34;Goodbye World\\n\u0026#34;);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e+  printf(\u0026#34;Hello World\\n\u0026#34;);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e\u003c/span\u003e   return 0;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e }\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn order to test out the patch, let us revert the hello-world.c file to it\u0026rsquo;s original state.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecp hello-world.c.orig hello-world.c\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow apply the patch\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epatch \u0026lt; hello.patch\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo undo the patch\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epatch -R hello-world.c \u0026lt; hello.patch\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2024.05.18",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/learning-language/",
    "url": "https://brandonrozek.com/blog/learning-language/",
    "title": "Learning a new language",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLast year I travelled to Italy for a conference. Right before the trip, I did what every great tourist does and tried to learn some Italian phrases.\u003c/p\u003e\n\u003cp\u003eThis experience inspired me to work towards learning more Spanish again. My ancestry is one reason, though another strong reason is that Spanish is the second most widely spoken language in the United States.\u003c/p\u003e\n\u003cp\u003eAs a child, I learned a mix of Spanish, French, and German in school. Sadly like many Americans, I did not retain much of what I learned. This is mostly because there aren\u0026rsquo;t many opportunities for me to use these languages from day-to-day. Everyone I interact with speaks English and many don\u0026rsquo;t know the three languages I was taught.\u003c/p\u003e\n\u003cp\u003eGiven that there\u0026rsquo;s some level of upkeep for retaining a language. It doesn\u0026rsquo;t make sense for me to diversify and try to learn multiple languages. My hope is that by focusing on Spanish, I can at least get to a level where I can hold a simple conversation confidently.\u003c/p\u003e\n\u003cp\u003eThe \u003ca href=\"https://en.wikipedia.org/wiki/Common_European_Framework_of_Reference_for_Languages\"\u003eCommon European Framework of Reference (CEFR) for languages\u003c/a\u003e provides a list of levels which mark a learner\u0026rsquo;s proficiency in a language. Roughly speaking, A is a \u003cem\u003ebasic user\u003c/em\u003e, B is an \u003cem\u003eindependent user\u003c/em\u003e, and C is a \u003cem\u003eproficient user\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eWhen most people learn a language, they strive to be fluent. They want to hit the C2 level of proficiency. I find this to be a somewhat unrealistic goal for myself as someone who doesn\u0026rsquo;t interact much with Spanish speakers.\u003c/p\u003e\n\u003cp\u003eFor one, C2 proficiency requires advance analysis on field specific issues. I\u0026rsquo;m not currently looking to perform academic research in Spanish. Instead, I wish to be able to hold simple conversations with other Spanish speakers.\u003c/p\u003e\n\u003cp\u003eTo help me on this journey, I took the mainstream route and downloaded Duolingo. There\u0026rsquo;s a lot of criticism online about the app\u0026rsquo;s focus on \u003ca href=\"https://en.wikipedia.org/wiki/Spaced_repetition\"\u003espaced repetition\u003c/a\u003e and psychological tactics it uses to pressure users to log on everyday.\u003c/p\u003e\n\u003cp\u003eI think, however, that it\u0026rsquo;s important to find what works for you. I found it easy to use the app to build a habit of studying Spanish since they create easily digestible lessons.\u003c/p\u003e\n\u003cp\u003eThe app is also not a one-size fits all. I find that the app is a little slow in exposing the learner to new vocabulary. The reading and radio exercises are also not as challenging as watching a TV show or reading a book in Spanish.\u003c/p\u003e\n\u003cp\u003eWhat I should really be doing, is watching \u003ca href=\"https://en.wikipedia.org/wiki/Telenovela\"\u003etelenovelas\u003c/a\u003e.\u003c/p\u003e\n",
    "date_published": "2024.05.12",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/migrating-docker-compose-podman-quadlets/",
    "url": "https://brandonrozek.com/blog/migrating-docker-compose-podman-quadlets/",
    "title": "Migrating from Docker Compose for Podman to Quadlets",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eTechnology never stagnates. There\u0026rsquo;s always people working hard to improve the current status quo. While it might be annoying at times, it does keep life exciting.\u003c/p\u003e\n\u003cp\u003eThe latest change, is that for systems where I use Podman containers, I now no longer use docker-compose but instead rely on Podman Quadlets which are managed by systemd. This means one less dependency on the docker toolchain, and we all accepted the systemd future by now right?\u003c/p\u003e\n\u003cp\u003eMo wrote a \u003ca href=\"https://mo8it.com/blog/quadlet/\"\u003eblog post\u003c/a\u003e earlier this year outlining how they work. I won\u0026rsquo;t reiterate that great work so I encourage you to check it out. Instead, I\u0026rsquo;ll paste one systemd configuration file of a podman container for future me to easily reference.\u003c/p\u003e\n\u003cp\u003eHedgedoc Container file at \u003ccode\u003e/etc/containers/systemd/hedgedoc.container\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Container]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eContainerName\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ehedgedoc\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eHostName\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ehedgedoc\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eImage\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003elscr.io/linuxserver/hedgedoc:1.9.9\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAutoUpdate\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eregistry\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eVolume\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/home/rozek/Volumes/hedgedoc/config:/config\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eEnvironmentFile\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/home/brozek/Volumes/hedgedoc/docker.env\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eIP\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e10.77.1.91\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eNetwork\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003erozeknet\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRequires\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ehedgedoc_database.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAfter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ehedgedoc_database.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRestart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ealways\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edefault.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2024.05.09",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/circling-around-vancouver/",
    "url": "https://brandonrozek.com/blog/circling-around-vancouver/",
    "title": "Circling around Vancouver",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eDowntown Vancouver is situated in a peninsula. With a day to explore, James and I walked, boated, bussed, and biked around the city! It\u0026rsquo;s very easy to get around without having a car and there\u0026rsquo;s a lot to explore. I hope I have the chance to revisit soon.\u003c/p\u003e\n\u003cp\u003eThe following map (provided by \u003ca href=\"https://www.openstreetmap.org/\"\u003eOpenStreetMap\u003c/a\u003e) shows some of the highlights of the journey.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/vancouver_map_landmarks.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eLandmarks:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eGranville Island\u003c/li\u003e\n\u003cli\u003eScience World\u003c/li\u003e\n\u003cli\u003eChinatown\u003c/li\u003e\n\u003cli\u003eGastown\u003c/li\u003e\n\u003cli\u003eWaterfront\u003c/li\u003e\n\u003cli\u003eThe Shipyards\u003c/li\u003e\n\u003cli\u003eStanley Park\u003c/li\u003e\n\u003cli\u003eEnglish Bay Beach\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eWe stayed at a hotel near the Yaletown-Roadhouse station. We started the day by walking across the bridge to Granville market.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/PXL_20240226_183733824.MP.jpg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eThis area has a lot of tourist-y souvenir shops. Great place to snag a few postcards. We had an early lunch at the \u003ca href=\"https://granvilleisland.com/public-market\"\u003eGranville Island Public Market\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/PXL_20240226_185801705.jpg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eGranville Island is situated at False Creek. Luckily the next stop on our journey, the Science Center, is also on this creek. The most fun way to get between the two places is to take a ferry.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/PXL_20240226_192303724.jpg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eThe last stop of the ferry is the Science Center. You can\u0026rsquo;t miss it, there\u0026rsquo;s a giant steel colored ball on top. The museum is highly interactive and is designed to impress. I learned there that \u003ca href=\"https://en.wikipedia.org/wiki/Smart_glass\"\u003eSmart glass\u003c/a\u003e exists.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/PXL_20240220_225006159.jpg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eFrom the Science Center, we took a short walk to Chinatown. We didn\u0026rsquo;t end up doing too much there, mostly took in the scenery.\u003c/p\u003e\n\u003cp\u003eWe then took a bus to Gastown. Every hour the \u003ca href=\"https://www.atlasobscura.com/places/steam-clock-gastown-vancouver\"\u003elocal steamclock\u003c/a\u003e plays its version of the Westminster Chime.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/PXL_20240226_214940118.jpg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eWe walked over to the Waterfront station to take a boat over to North Vancouver.\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e  The false creek ferry was small. This boat, however, wasn\u0026rsquo;t. I walked in and found it hilarious how it looks like your standard metro or bus.\u003csup id=\"fnref:2\"\u003e\u003ca href=\"#fn:2\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e2\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/PXL_20240226_223129833.jpg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eAt North Vancouver we visited the Shipyards. It provides what I think is one of the best views of the Vancouver City Center skyline.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/PXL_20240226_230135004.MP.jpg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eWe ate food at a local pizza place, and visited a few of the local bookstores in this area. The local bus system then took us to Stanley Park.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/PXL_20240227_003904275.jpg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eStanley Park is a must see if you\u0026rsquo;re ever in Vancouver. The \u003ca href=\"https://en.wikipedia.org/wiki/Seawall_(Vancouver)\"\u003eVancouver Seawall\u003c/a\u003e surrounds the perimeter and offers stunning view of the city and the local mountains.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/PXL_20240227_012920333.jpg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eThe park is large. To make the most of the journey, I recommend biking the path. When we went there were easily accessible bike rentals.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/PXL_20240227_013849323.jpg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eAfter exiting the perimeter of the park on the left side, we were looking for a place to return our bikes. Unfortunately a lot of the bike return spots were full! We luckily found exactly two empty spots at the English Bay Beach.\u003c/p\u003e\n\u003cp\u003eEven though the bike ride was extremely rewarding, it was also physically draining. We didn\u0026rsquo;t stay too long at the beach, and instead took an Uber to the hotel to rest.\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eIn case you didn\u0026rsquo;t notice, I really enjoy boat rides.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:2\"\u003e\n\u003cp\u003eMust be because it is part of the public transportation network.\u0026#160;\u003ca href=\"#fnref:2\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2024.05.04",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/usps-forever-stamps/",
    "url": "https://brandonrozek.com/blog/usps-forever-stamps/",
    "title": "USPS Forever Stamps",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI don\u0026rsquo;t write and send physical letters often, but if there is anything that would make me reconsider then it would the USPS forever stamps.\u003c/p\u003e\n\u003cp\u003eForever stamps are first class one-ounce stamps. The price to ship these have gone up over time, but Forever stamps are always valid. The \u003ca href=\"https://about.usps.com/news/fact-sheets/forever-stamp-facts.htm\"\u003eUSPS fact sheet\u003c/a\u003e shows that it started back in 2007.\u003c/p\u003e\n\u003cp\u003eOne of the coolest things about Forever stamps is that their design isn\u0026rsquo;t static. In fact, they release new designs often and multiple are usually for sale at a time. As of the time of writing, you can purchase them online at this \u003ca href=\"https://store.usps.com/store/results/forever/stamps/_/N-1uq0torZ9y93lv\"\u003elink\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThe latest stamps to come out in recent months are:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://about.usps.com/newsroom/national-releases/2024/0315-postal-service-pays-tribute-to-bluegrass-music.htm\"\u003eBluegrass\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://about.usps.com/newsroom/national-releases/2024/0306-usps-unveils-betty-ford-stamp.htm\"\u003eBetty Ford\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://about.usps.com/newsroom/national-releases/2024/0229ma-new-save-manatees-postage-stamp-to-be-issued-on-manatee-appreciation-day.htm\"\u003eSave Manatees\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://about.usps.com/postal-bulletin/2024/pb22643/html/info_008.htm\"\u003eWedding Blooms\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://about.usps.com/newsroom/national-releases/2024/0309-usps-pays-homage-to-heroes-of-the-underground-railroad.htm\"\u003eUnderground Railroad\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://about.usps.com/newsroom/national-releases/2024/0224-usps-dedicates-forever-stamp-honoring-ucla-coach-john-wooden.htm\"\u003eJohn Wooden\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIn recent years, Clare and I\u0026rsquo;s favorite stamps are:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://about.usps.com/newsroom/national-releases/2023/0309-new-forever-stamps-commemorate-railroad-stations.htm\"\u003eRailroad Stations\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://about.usps.com/newsroom/national-releases/2023/1010-usps-issues-winter-woodland-animals-stamps.htm\"\u003eWinter Woodland Animals\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://about.usps.com/newsroom/national-releases/2019/0709-usps-celebrates-four-species-of-frogs.htm\"\u003eFrog Stamps\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThough really Clare says the Frog stamps are the best of all. I mean really, can you resist these?\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/202404252216.jpg\" alt=\"\"\u003e\u003c/p\u003e\n",
    "date_published": "2024.04.25",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/tracking-software-releases-rss/",
    "url": "https://brandonrozek.com/blog/tracking-software-releases-rss/",
    "title": "Tracking Software Releases via RSS",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI have \u003ca href=\"https://docs.fedoraproject.org/en-US/quick-docs/autoupdates/\"\u003eautomatic updates\u003c/a\u003e set up on most of my machines. It\u0026rsquo;s great for the majority of software packages that don\u0026rsquo;t require manual intervention.\u003c/p\u003e\n\u003cp\u003eOn the other hand some of my self-hosted services (and even my OPNsense router), require me to be slightly more diligent. That is to say, I have enough scars from letting my databases auto-update. I now make sure to check the migration notes.\u003c/p\u003e\n\u003cp\u003eHowever promptness-wise, I was not great.  Usually a few months would go by before the thought of software updates came to mind. From there, I would manually check the current software version of each software service I was currently running.\u003c/p\u003e\n\u003cp\u003eNowadays, that is no more! I am a completely changed person. Thanks to the power of RSS.\u003c/p\u003e\n\u003cp\u003eGitHub automatically generates RSS feeds for the releases page (as well as tags) for all of their hosted repositories. Plop that into your favorite RSS reader, and you can get notified whenever a new version is released!\u003c/p\u003e\n\u003cp\u003eEx: \u003ca href=\"https://github.com/nextcloud/server/releases\"\u003ehttps://github.com/nextcloud/server/releases\u003c/a\u003e\u003c/p\u003e\n",
    "date_published": "2024.04.21",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/disjunctive-goals-pddl/",
    "url": "https://brandonrozek.com/blog/disjunctive-goals-pddl/",
    "title": "Disjunctive Goals in PDDL",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eClassical AI planning tries to find a sequence of actions that takes an agent from their initial state to a state that satisfies some goal.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s say, for example,  that you want to deliver a package that\u0026rsquo;s currently sitting at location \u003ccode\u003eA\u003c/code\u003e. The recipient said that you can drop it off at either their workplace \u003ccode\u003eB\u003c/code\u003e or their home \u003ccode\u003eC\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eIf you drop it off at a some location, then people will be nice enough to let you retrieve the package and place it back at location \u003ccode\u003eA\u003c/code\u003e.  Graphically, we can represent it as follows:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/disjunctive-packages.svg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eA state in classical planning is a set of atomic formulas. For our problem it will look like:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-lisp\" data-lang=\"lisp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e(\u003cspan style=\"color:#e6db74\"\u003e:init\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (at-package A) \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \t\u003cspan style=\"color:#75715e\"\u003e; Delivery Paths\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (CONNECTED A B)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (CONNECTED A C)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (CONNECTED A D)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \t\u003cspan style=\"color:#75715e\"\u003e; Retrieval paths\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (CONNECTED B A)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \t(CONNECTED C A)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \t(CONNECTED D A)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe goal in classical planning is a set of ground atomic formulas and the negations of ground atomic formulas. That means, the following isn\u0026rsquo;t a \u0026ldquo;valid\u0026rdquo; goal in the classical planning model.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-lisp\" data-lang=\"lisp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e(\u003cspan style=\"color:#e6db74\"\u003e:goal\u003c/span\u003e (or (at-package B) (at-package C)))\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI put valid in quotation marks since if you use the \u003ca href=\"https://www.fast-downward.org/\"\u003eFast Downward\u003c/a\u003e planner, it will behind the scenes compile it to a valid planning problem. But what if you\u0026rsquo;re not fortunate enough to have a planner automatically do that for you? We\u0026rsquo;ll discuss three different techniques for handling disjunctive goals.\u003c/p\u003e\n\u003ch2 id=\"1-plan-for-each-disjunct\"\u003e(1) Plan for each Disjunct\u003c/h2\u003e\n\u003cp\u003eA plan that delivers a package to \u003ccode\u003eB\u003c/code\u003e is valid for the goal of delivering it to either \u003ccode\u003eB\u003c/code\u003e or \u003ccode\u003eC\u003c/code\u003e. The same goes for delivering it to \u003ccode\u003eC\u003c/code\u003e. Therefore, the set of valid plans for the disjunctive goal is the union of all the plans that satisfy either of the disjuncts.\u003c/p\u003e\n\u003cp\u003eDomain File:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-lisp\" data-lang=\"lisp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e(define (domain test-disjunct)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:requirements\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e:typing\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:types\u003c/span\u003e location \u003cspan style=\"color:#a6e22e\"\u003e-\u003c/span\u003e object)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:predicates\u003c/span\u003e (at-package ?l \u003cspan style=\"color:#a6e22e\"\u003e-\u003c/span\u003e location) (CONNECTED ?l1 ?l2 \u003cspan style=\"color:#a6e22e\"\u003e-\u003c/span\u003e location)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:action\u003c/span\u003e move-package\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e:parameters\u003c/span\u003e (?l1 ?l2 \u003cspan style=\"color:#a6e22e\"\u003e-\u003c/span\u003e location)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e:precondition\u003c/span\u003e (and \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            (at-package ?l1) \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            (CONNECTED ?l1 ?l2)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e:effect\u003c/span\u003e (and \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            (at-package ?l2)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            (\u003cspan style=\"color:#a6e22e\"\u003enot\u003c/span\u003e (at-package ?l1))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eProblem File:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-lisp\" data-lang=\"lisp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e(define (problem test-disjunct)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:domain\u003c/span\u003e test-disjunct)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:requirements\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e:typing\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:objects\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        A B C D \u003cspan style=\"color:#a6e22e\"\u003e-\u003c/span\u003e location\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:init\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \t(at-package A) \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \t(CONNECTED A B)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \t(CONNECTED A C)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \t(CONNECTED A D)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \t(CONNECTED B A)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \t\t(CONNECTED C A)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \t\t(CONNECTED D A)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:goal\u003c/span\u003e (at-package B))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e; (:goal (at-package C))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e) \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eCall Planner:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e./fast-downward.sif --alias lama-first domain-disjunct.pddl problem-disjunct.pddl\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eResult:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003emove-package a b\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"2-derived-predicates-axioms\"\u003e(2) Derived Predicates (Axioms)\u003c/h2\u003e\n\u003cp\u003eAxioms or derived predicates are atomic ground formula that can only appear in the precondition of actions and the goal. That is, it cannot appear in the effect.\u003c/p\u003e\n\u003cp\u003eWe can create a new derived predicate \u003ccode\u003edone\u003c/code\u003e which when true means that we have satisfied the goal.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-lisp\" data-lang=\"lisp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e(\u003cspan style=\"color:#e6db74\"\u003e:derived\u003c/span\u003e (done) (or (at-package B) (at-package C)))\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDomain File:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-lisp\" data-lang=\"lisp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e(define (domain test-derived)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:requirements\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e:derived-predicates\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e:typing\u003c/span\u003e )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:types\u003c/span\u003e location \u003cspan style=\"color:#a6e22e\"\u003e-\u003c/span\u003e object)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:predicates\u003c/span\u003e (at-package ?l \u003cspan style=\"color:#a6e22e\"\u003e-\u003c/span\u003e location) (CONNECTED ?l1 ?l2 \u003cspan style=\"color:#a6e22e\"\u003e-\u003c/span\u003e location) (done))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:derived\u003c/span\u003e (done) (or (at-package B) (at-package C)))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:action\u003c/span\u003e move-package\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e:parameters\u003c/span\u003e (?l1 ?l2 \u003cspan style=\"color:#a6e22e\"\u003e-\u003c/span\u003e location)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e:precondition\u003c/span\u003e (and \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            (at-package ?l1) \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            (CONNECTED ?l1 ?l2)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e:effect\u003c/span\u003e (and \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            (at-package ?l2)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            (\u003cspan style=\"color:#a6e22e\"\u003enot\u003c/span\u003e (at-package ?l1))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eProblem File:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-lisp\" data-lang=\"lisp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e(define (problem test-derived)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:domain\u003c/span\u003e test-derived)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:requirements\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e:typing\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:objects\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        A B C D \u003cspan style=\"color:#a6e22e\"\u003e-\u003c/span\u003e location\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:init\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \t(at-package A) \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \t(CONNECTED A B)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \t(CONNECTED A C)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \t(CONNECTED A D)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \t(CONNECTED B A)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \t\t(CONNECTED C A)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \t\t(CONNECTED D A)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:goal\u003c/span\u003e (done))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e) \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eCall Planner:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e./fast-downward.sif benchmarks/domain-derived.pddl benchmarks/problem-derived.pddl \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t--search \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;astar(blind())\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eResult:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003emove-package a b\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003eNote:\u003c/strong\u003e Not all heuristics in FastDownward support axioms. In fact this problem is not limited to FastDownward. The planner call above uses the blind heuristic which supports axioms and is safe.\u003c/p\u003e\n\u003cp\u003eBefore replacing that evaluator, however, make sure to \u003ca href=\"https://www.fast-downward.org/Doc/Evaluator\"\u003eread the docs\u003c/a\u003e to make sure that the heuristic states that it supports axioms, and that it is safe in the presence of them.\u003c/p\u003e\n\u003ch2 id=\"3-compilation\"\u003e(3) Compilation\u003c/h2\u003e\n\u003cp\u003eThe core idea of this technique is to push the disjunction onto the search space. As with the last technique we create a new predicate \u003ccode\u003edone\u003c/code\u003e. However, instead of automatically deriving \u003ccode\u003edone\u003c/code\u003e through an axiom, we create an action for every disjunct whose precondition is that disjunct, and whose effect is \u003ccode\u003edone\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eExample:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-lisp\" data-lang=\"lisp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e(\u003cspan style=\"color:#e6db74\"\u003e:action\u003c/span\u003e goal-B\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e:parameters\u003c/span\u003e ()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e:precondition\u003c/span\u003e (at-package B)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e:effect\u003c/span\u003e (done)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e(\u003cspan style=\"color:#e6db74\"\u003e:action\u003c/span\u003e goal-C\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e:parameters\u003c/span\u003e ()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e:precondition\u003c/span\u003e (at-package C)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e:effect\u003c/span\u003e (done)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOur new goal will be \u003ccode\u003e(:goal (done))\u003c/code\u003e.  After you plan using the compiled version of the problem, drop the actions \u003ccode\u003egoal-B\u003c/code\u003e and \u003ccode\u003egoal-C\u003c/code\u003e and you have a plan for the disjunctive problem.\u003c/p\u003e\n\u003cp\u003eThis technique works great when you\u0026rsquo;re grabbing the first optimal plan, however, there are two considerations that need to be made when you grab more than just one plan\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e.\u003c/p\u003e\n\u003cp\u003e(1) There\u0026rsquo;s nothing guarding against an infinite chain of \u003ccode\u003egoal-B\u003c/code\u003e action calls. Hence the following is a valid plan:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003emove-package a b\ngoal-B\ngoal-B\ngoal-B\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe can address this by adding \u003ccode\u003e(not (done))\u003c/code\u003e to the preconditions of each of the goal actions.\u003c/p\u003e\n\u003cp\u003e(2) We have no effects that remove \u003ccode\u003edone\u003c/code\u003e, so a valid plan can move to a state that satisfies the goal and then move away.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003emove-package a b\nmove-package b a\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe can address this by adding \u003ccode\u003e(not (done))\u003c/code\u003e to the effect of every action that is not the goal actions.\u003c/p\u003e\n\u003cp\u003eDomain File:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-lisp\" data-lang=\"lisp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e(define (domain test-compile)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:requirements\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e:typing\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:types\u003c/span\u003e location \u003cspan style=\"color:#a6e22e\"\u003e-\u003c/span\u003e object)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:predicates\u003c/span\u003e (at-package ?l \u003cspan style=\"color:#a6e22e\"\u003e-\u003c/span\u003e location) (CONNECTED ?l1 ?l2 \u003cspan style=\"color:#a6e22e\"\u003e-\u003c/span\u003e location) (done))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:action\u003c/span\u003e move-package\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e:parameters\u003c/span\u003e (?l1 ?l2 \u003cspan style=\"color:#a6e22e\"\u003e-\u003c/span\u003e location)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e:precondition\u003c/span\u003e (and \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            (at-package ?l1) \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            (CONNECTED ?l1 ?l2)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e:effect\u003c/span\u003e (and \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            (at-package ?l2)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            (\u003cspan style=\"color:#a6e22e\"\u003enot\u003c/span\u003e (at-package ?l1))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            (\u003cspan style=\"color:#a6e22e\"\u003enot\u003c/span\u003e (done))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:action\u003c/span\u003e goal-B\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e:parameters\u003c/span\u003e ()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e:precondition\u003c/span\u003e (and\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            (at-package B)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            (\u003cspan style=\"color:#a6e22e\"\u003enot\u003c/span\u003e (done))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e:effect\u003c/span\u003e (done)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:action\u003c/span\u003e goal-C\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e:parameters\u003c/span\u003e ()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e:precondition\u003c/span\u003e (and \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            (at-package C)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            (\u003cspan style=\"color:#a6e22e\"\u003enot\u003c/span\u003e (done))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e:effect\u003c/span\u003e (done)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eProblem File:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-lisp\" data-lang=\"lisp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e(define (problem test-compile)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:domain\u003c/span\u003e test-compile)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:requirements\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e:strips\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:objects\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        A B C D \u003cspan style=\"color:#a6e22e\"\u003e-\u003c/span\u003e location\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:init\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \t(at-package A) \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \t(CONNECTED A B)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \t(CONNECTED A C)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \t(CONNECTED A D)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \t(CONNECTED B A)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \t\t(CONNECTED C A)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \t\t(CONNECTED D A)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    (\u003cspan style=\"color:#e6db74\"\u003e:goal\u003c/span\u003e (done))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e) \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003ePlanner Call:\u003c/p\u003e\n\u003cp\u003eTo retrieve multiple plans, we can use the \u003ca href=\"https://github.com/IBM/kstar\"\u003eKstar\u003c/a\u003e package by IBM. In particular, I had success with their \u003ca href=\"https://pypi.org/project/kstar-planner/\"\u003ePython package\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e kstar_planner \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e planners\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e pathlib \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e Path\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edomain_file \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Path(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;domain-compile.pddl\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eproblem_file \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Path(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;problem-compile.pddl\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplans \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e planners\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eplan_topk(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    domain_file\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003edomain_file,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    problem_file\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eproblem_file,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    number_of_plans_bound\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    timeout\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e30\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(plans)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOutput:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e===\nmove-package a b\ngoal-b\n===\nmove-package a c\ngoal-c\n===\nmove-package a b\nmove-package b a\nmove-package a b\ngoal-b\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNot sure why you would want to execute the last plan, but at least the package got to its destination at the end :D\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eThe field of diverse planning looks at generating multiple plans for a given planning problem. The introduction to \u003ca href=\"https://ojs.aaai.org/index.php/AAAI/article/view/6543\"\u003e\u0026ldquo;Reshaping Diverse Planning\u0026rdquo;\u003c/a\u003e by Michael Katz and Shirin Sohrabi covers well the motivations behind this.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2024.04.16",
    "tags": ["Automated Planning"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/functions-inductive-propositions-lean4/",
    "url": "https://brandonrozek.com/blog/functions-inductive-propositions-lean4/",
    "title": "Functions and Inductive Propositions in Lean 4",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis blog post is inspired by the book \u003ca href=\"https://softwarefoundations.cis.upenn.edu/lf-current/index.html\"\u003eLogical Foundations\u003c/a\u003e where it goes over similar content in the Coq proof assistant. Instead, we will look at Lean 4.\u003c/p\u003e\n\u003cp\u003eFunctions in Lean are required to be total and terminating. This means that there are some mathematical functions that cannot be represented. An example of which is the collatz conjecture.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003edef collatz_fail (n : Nat) : Nat :=\n  match n with\n  | (0: Nat) =\u0026gt; (0: Nat)\n  | (1: Nat) =\u0026gt; (1: Nat)\n  | x =\u0026gt;\n      if x % 2 = 0 then collatz_fail (x / 2)\n      else collatz_fail (3 * x + 1)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIf you type the above function in Lean, you\u0026rsquo;ll get the following error:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efail to show termination for\n  collatz_fail\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eI can\u0026rsquo;t tell you how we would go about showing termination. After all, many brilliant mathematicians are attempting to solve this \u003ca href=\"https://en.wikipedia.org/wiki/Collatz_conjecture\"\u003econjecture\u003c/a\u003e. Instead, we can represent the collatz procedure as an inductive proposition.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003einductive collatz : Nat → Nat → Prop where\n  | c0 : collatz 0 0\n  | c1 : collatz 1 1\n  | ceven (n r : Nat) : ¬(n = 0) → n % 2 = 0 → collatz (n / 2) r → collatz n r\n  | codd (n r : Nat) : ¬(n = 1) → ¬(n % 2 = 0) → collatz (3 * n + 1) r → collatz n r\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eOur inductive proposition \u003ccode\u003ecollatz\u003c/code\u003e is built with the first argument representing our input \u003ccode\u003en\u003c/code\u003e and the second argument representing the result. Note that the first two constructors \u003ccode\u003ec0\u003c/code\u003e and \u003ccode\u003ec1\u003c/code\u003e denote the base cases. The second two constructors \u003ccode\u003eceven\u003c/code\u003e and \u003ccode\u003ecodd\u003c/code\u003e have various conditions on them:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eFor \u003ccode\u003eceven\u003c/code\u003e, we first require that $n \\ne 0$, and that $n$ is even.  If that\u0026rsquo;s the case, then whatever \u003ccode\u003er\u003c/code\u003e is in the inductive proposition \u003ccode\u003ecollatz (n / 2) r\u003c/code\u003e, that \u003ccode\u003er\u003c/code\u003e will be the result in \u003ccode\u003ecollatz n r\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eSimilarly, for \u003ccode\u003ecodd\u003c/code\u003e, the inductive proposition requires that $n \\ne 1$ and that $n$ is odd. Then \u003ccode\u003er\u003c/code\u003e holds whatever value \u003ccode\u003ecollatz (3 * n + 1) r\u003c/code\u003e holds.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eTo use this inductive proposition, we need to build up a proof.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample : collatz 5 1 := by\n  apply collatz.codd; trivial; trivial\n  show collatz 16 1\n  apply collatz.ceven; trivial; trivial\n  show collatz 8 1\n  apply collatz.ceven; trivial; trivial\n  show collatz 4 1\n  apply collatz.ceven; trivial; trivial\n  show collatz 2 1\n  apply collatz.ceven; trivial; trivial\n  show collatz 1 1\n  apply collatz.c1\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFor inductive propositions, Lean does not require that it\u0026rsquo;s definition is total or terminating. In fact, in general perhaps a different ordering of constructors can be called in order to produce two different results for \u003ccode\u003er\u003c/code\u003e! Our specific definition, however, only produces one \u003ccode\u003er\u003c/code\u003e for a given \u003ccode\u003en\u003c/code\u003e, and we can prove that in Lean.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem collatz_deterministic (n r1 r2: Nat) (H1: collatz n r1) (H2: collatz n r2) : r1 = r2 := by\n  revert r2\n  -- Look at each of the possible\n  -- constructors for collatz\n  induction H1\n  case c0 =\u0026gt;\n    intro r\n    intro (H: collatz 0 r)\n    cases H\n    case c0 =\u0026gt; rfl\n    -- automatically detects c1 as a contradiction\n    case ceven =\u0026gt; contradiction\n    case codd =\u0026gt; contradiction\n\n  case c1 =\u0026gt;\n    intro r\n    intro (H: collatz 1 r)\n    cases H\n    -- automatically detects c0 as a contradiction\n    case c1 =\u0026gt; rfl\n    case ceven =\u0026gt; contradiction\n    case codd =\u0026gt; contradiction\n\n  case ceven n r1 N0 NE H IH =\u0026gt;\n    -- N0 : n ≠ 0\n    -- NE : n % 2 = 0\n    -- IH : ∀ (r2 : ℕ), collatz (n / 2) r2 → r1 = r2\n    intro r2\n    intro (H: collatz n r2)\n    cases H\n    case c0 =\u0026gt; contradiction\n    case c1 =\u0026gt; contradiction\n    case ceven H2 =\u0026gt; apply IH r2 H2\n    case codd =\u0026gt; contradiction\n\n  case codd n r1 N1 NO H IH =\u0026gt;\n    -- N1 : n ≠ 1\n    -- NO : n % 2 = 1\n    -- IH : ∀ (r2 : ℕ), collatz (3 * n + 1) r2 → r1 = r2\n    intro r2\n    intro (H: collatz n r2)\n    cases H\n    case c0 =\u0026gt; contradiction\n    case c1 =\u0026gt; contradiction\n    case ceven =\u0026gt; contradiction\n    case codd H2 =\u0026gt; apply IH r2 H2\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis means that we don\u0026rsquo;t have to worry about calling the wrong constructor. If it fails, we backtrack and try a different one. This leads to an effective proof strategy:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample : collatz 5000 1 := by\n  repeat (first |\n    apply collatz.c0 |\n    apply collatz.c1 |\n    apply collatz.codd; trivial; trivial; simp |\n    apply collatz.ceven; trivial; trivial; simp\n  )\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eLet\u0026rsquo;s return to our function \u003ccode\u003ecollatz_fail\u003c/code\u003e. The reason this function didn\u0026rsquo;t work is that it wasn\u0026rsquo;t obvious that the function terminates. We can force it to terminate by introducing a new variable \u003ccode\u003et\u003c/code\u003e that denotes the number of remaining steps before giving up and returning \u003ccode\u003enone\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003edef collatz_fun (n t : Nat) : Option Nat :=\n  if t = 0 then none else\n  match n with\n  | (0: Nat) =\u0026gt; (0: Nat)\n  | (1: Nat) =\u0026gt; (1: Nat)\n  | x =\u0026gt;\n      if x % 2 = 0 then collatz_fun (x / 2) (t - 1)\n      else collatz_fun (3 * x + 1) (t - 1)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eInstead of having to do the proof search from above, we can call \u003ccode\u003eeval\u003c/code\u003e on this function\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003e#eval collatz_fun 5 6\n#eval collatz_fun 5000 29\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIs there any relationship between the inductive proposition version and the function we just built? Ideally we would want to prove the following relationship:\n$$\n\\exists t, collatzfun(n , t) = some(r) \\iff collatz(n, r)\n$$\nSince we don\u0026rsquo;t know how to show whether collatz terminates, we can\u0026rsquo;t at this time prove the backwards direction. We can, however, prove the forward direction through induction on the number of steps.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem collatz_sound (n r : Nat) : (∃ t, collatz_fun n t = some r) → collatz n r := by\n  intro (H: ∃ t, collatz_fun n t = some r)\n  obtain ⟨t, H⟩ := H\n  revert H n r\n  show ∀ (n r : Nat), collatz_fun n t = some r → collatz n r\n\n  let motive := fun x : Nat =\u0026gt; ∀ (n r: Nat), collatz_fun n x = some r → collatz n r\n\n  apply Nat.recOn (motive := motive) t\n\n  -- t = 0\n  case intro.zero =\u0026gt;\n    intro r n\n    intro (HFalse : collatz_fun r 0 = some n)\n    contradiction\n\n  -- t = S t1\n  case intro.succ =\u0026gt;\n    intro t\n    intro (IH : ∀ (n r : ℕ), collatz_fun n t = some r → collatz n r)\n    intro n r\n    intro (H : collatz_fun n (t + 1) = some r)\n    show collatz n r\n\n    unfold collatz_fun at H\n    -- Go into else case since (t1 + 1 ≠ 0)\n    simp [ite_false] at H\n    -- Consider each case for n\n    split at H\n\n    -- n = 0\n    case h_1 =\u0026gt;\n        -- H : some 0 = some r\n        have H : 0 = r := by\n          rewrite [Option.some.injEq] at H; assumption\n        suffices collatz 0 0 by\n          rewrite [\u0026lt;- H]; assumption\n        apply collatz.c0\n\n    -- n = 1\n    case h_2 =\u0026gt;\n        -- H : some 1 = some r\n        have H : 1 = r := by\n          rewrite [Option.some.injEq] at H; assumption\n        suffices collatz 1 1 by\n          rewrite [\u0026lt;- H]; assumption\n        apply collatz.c1\n\n    case h_3 N0 N1 =\u0026gt;\n      -- N0 : n ≠ 0\n      -- N1 : n ≠ 1\n      split at H\n      -- n % 2 = 0\n      case inl NE =\u0026gt;\n      \t-- NE : n % 2 = 0\n        -- H : collatz_fun (n / 2) t = some r\n        suffices collatz (n / 2) r by\n          apply collatz.ceven n r N0 NE; assumption\n        apply IH (n / 2) r H\n\n      -- n % 2 = 1\n      case inr NO =\u0026gt;\n      \t-- NO : n % 2 = 1\n        -- H : collatz_fun (3 * n + 1) t = some r\n        suffices collatz (3 * n + 1) r by\n          apply collatz.codd n r N1 NO; assumption\n        apply IH (3 * n + 1) r H\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2024.04.14",
    "tags": ["Formal Methods"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/disabling-cpus-save-power/",
    "url": "https://brandonrozek.com/blog/disabling-cpus-save-power/",
    "title": "Disabling CPUs to Save Power",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLooking for ways to reduce the power usage of my home server? This post shows a not-so-scientific look at how disabling CPUs can help save some power.\u003c/p\u003e\n\u003cp\u003eI run a Dell PowerEdge R430 with an Intel Xeon E5-2643 v3 CPU. This gives me 12 physical cores, and with hyperthreading this presents itself as 24 logical cores.\u003c/p\u003e\n\u003cp\u003eGiven that I mostly use my server as media storage, most of those CPUs sit idle most of the time. My thought is, how much power can I save if I disable some of these unused cores?\u003c/p\u003e\n\u003cp\u003eFor idle workloads disabling the CPU does not result in any noticeable power savings. The power savings is significant, however, if you analyze the system under load.\u003c/p\u003e\n\u003cp\u003eTo conduct this experiment, I used a \u003ca href=\"https://en.wikipedia.org/wiki/Kill_A_Watt\"\u003eKill a Watt\u003c/a\u003e measuring device which monitors the power usage of whatever is plugged into it.\u003c/p\u003e\n\u003cp\u003eTo disable a CPU in Linux, use the following command:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Disable CPU 23\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e | sudo tee /sys/devices/system/cpu/cpu23/online \u0026gt; /dev/null\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRepeat for any CPUs you want to disable. You can see which CPUs are available using \u003ccode\u003els /sys/devices/system/cpu\u003c/code\u003e. The \u003ccode\u003ehtop\u003c/code\u003e tool will display in addition to the CPU utilization, which CPUs are offline.\u003c/p\u003e\n\u003cp\u003eTo get the system under load, I used the \u003ccode\u003estress\u003c/code\u003e tool.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Spin off 24 dummy tasks that max out each CPU\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003estress -c \u003cspan style=\"color:#ae81ff\"\u003e24\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI ran the stress tool with the corresponding number of logical CPUs I had online to come up with the following table:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e# Online Logical CPU\u003c/th\u003e\n          \u003cth\u003ePower (Watts)\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e24\u003c/td\u003e\n          \u003ctd\u003e360\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e12\u003c/td\u003e\n          \u003ctd\u003e295\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e6\u003c/td\u003e\n          \u003ctd\u003e194\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e4\u003c/td\u003e\n          \u003ctd\u003e173\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eCutting down to 4 logical CPUs can cut the power usage under load in half! Do note though, that this exchanges performance for power savings. If you are running a low amount of services on your home server or can wait a bit of extra time for a computation to finish, consider disabling some of your CPUs.\u003c/p\u003e\n",
    "date_published": "2024.04.06",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/integer-sets-lean4/",
    "url": "https://brandonrozek.com/blog/integer-sets-lean4/",
    "title": "Working with integer sets in Lean 4",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eRecently I was convinced by \u003ca href=\"https://jamesoswald.dev/\"\u003eJames Oswald\u003c/a\u003e to work with him on proving some lemmas about specific integer sets in Lean 4. Since we have different styles in going about Lean proofs, I present my version here.\u003c/p\u003e\n\u003cp\u003eWe\u0026rsquo;ll go over three proofs today:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e$\\{x \\mid x \\in \\mathbb{Z} \\wedge x^2 = 9\\} = \\{-3, 3\\}$\u003c/li\u003e\n\u003cli\u003e$\\{x \\mid x \\in \\mathbb{Z} \\wedge -4 \\le n \\wedge n \\le 15 ∧ Even~n \\} = \\{-4, -2, 0, 2, 4, 6, 8, 10, 12, 14\\}$\u003c/li\u003e\n\u003cli\u003e$\\{x \\mid x \\in \\mathbb{Z} \\wedge x^2 = 6\\} = \\emptyset$\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"example-1\"\u003eExample 1\u003c/h2\u003e\n\u003cp\u003eFirst, let\u0026rsquo;s define our set $A$\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003edef A : Set Int := {x : Int | x^2 = 9}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe want to prove the following lemma:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003elemma instA : A = ({3, -3} : Finset ℤ)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eRecall that for sets, $A = S \\iff A \\subseteq S \\wedge S \\subseteq A$.\u003c/p\u003e\n\u003cp\u003eSince we\u0026rsquo;re given a specific Finset for $S$, it would be really nice if we can compute whether $S$ is a subset of $A$. In fact we can, as long as we prove a couple theorems first.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003einstance (n: Int) : Decidable (n ∈ A) := by\n  suffices Decidable (n^2 = 9) by\n    rewrite [A, Set.mem_setOf_eq]\n    assumption\n  apply inferInstance\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFirst as shown above, we need to decide whether an integer is in $A$. Given how $A$ is defined, this is equivalent to seeing if $n^2 = 9$. Luckily for us, the core of Lean already has a decision procedure for this. We can have Lean apply the appropriate one by calling \u003ccode\u003eapply inferInstance\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eWith this, we can now use \u003ccode\u003e#eval\u003c/code\u003e to see if elements are in $A$ or not.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003e#eval List.Forall (· ∈ A) [-3, 3]\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTo say that a finite set $S$ is a subset of $A$, is to say that all the elements of $S$ are in $A$. Using the last theorem, we can prove that checking subsets in this direction is decidable.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003einstance (S : Finset Int) : Decidable (↑S ⊆ A) := by\n  rewrite [A]\n  rewrite [Set.subset_def]\n  show Decidable (∀ x ∈ S, x ∈ {x | x ^ 2 = 9})\n  apply inferInstance\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eKeep in mind that at this point it\u0026rsquo;s not necessarily decidable if $A \\subseteq S$. This is because we haven\u0026rsquo;t established if $A$ is finite. However instead of trying to prove finiteness, let\u0026rsquo;s skip to the main proof and show that $A = \\{3, -3\\}$ classically.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003elemma instA : A = ({3, -3} : Finset ℤ) := by\n  let S : Finset ℤ := {3, -3}\n  change (A = ↑S)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAs stated before, set equality is making sure both are subsets of each other\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003esuffices A ⊆ ↑S ∧ ↑S ⊆ A by\n  rewrite [Set.Subset.antisymm_iff]\n  assumption\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eLets make use of our decidability proof to show $S \\subseteq A$ in one line!\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003ehave H2 : ↑S ⊆ A := by decide\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFor the other side,\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003ehave H1 : A ⊆ ↑S := by\n  intro (n : ℤ)\n  -- Goal is now (n ∈ A → n ∈ {3, -3})\n  intro (H1_1: n ∈ A)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eLet\u0026rsquo;s change \u003ccode\u003eH1_1\u003c/code\u003e to be in a form easier to work with\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003ehave H1_1 : n^2 = 9 := by\n  rewrite [A, Set.mem_setOf_eq] at H1_1\n  assumption\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTo show that $n \\in \\{3, -3\\}$, then it\u0026rsquo;s the same as saying that $n = 3$ or $n = -3$.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003esuffices n = 3 ∨ n = -3 by\n  show n ∈ S\n  rewrite [Finset.mem_insert, Finset.mem_singleton]\n  assumption\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe can show this using a theorem from mathlib!\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003eexact eq_or_eq_neg_of_sq_eq_sq n 3 H1_1\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003ca href=\"https://web.archive.org/web/20240106030817/https://leanprover-community.github.io/mathlib4_docs/Mathlib/Algebra/GroupPower/Ring.html#eq_or_eq_neg_of_sq_eq_sq\"\u003e(Quite the long name\u0026hellip;)\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eLastly, we combine the two subset proofs to show equality\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003eexact And.intro H1 H2\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAll together the proof looks like:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003elemma instA : A = ({3, -3} : Finset ℤ) := by\n  let S : Finset ℤ := {3, -3}\n  change (A = ↑S)\n\n  suffices A ⊆ ↑S ∧ ↑S ⊆ A by\n    rewrite [Set.Subset.antisymm_iff]\n    assumption\n\n  have H1 : A ⊆ ↑S := by\n    intro (n : ℤ)\n    -- Goal is now (n ∈ A → n ∈ {3, -3})\n    intro (H1_1: n ∈ A)\n    have H1_1 : n^2 = 9 := by\n      rewrite [A, Set.mem_setOf_eq] at H1_1\n      assumption\n\n    suffices n = 3 ∨ n = -3 by\n      show n ∈ S\n      rewrite [Finset.mem_insert, Finset.mem_singleton]\n      assumption\n\n    exact eq_or_eq_neg_of_sq_eq_sq n 3 H1_1\n\n  have H2 : ↑S ⊆ A := by decide\n\n  exact And.intro H1 H2\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"example-2\"\u003eExample 2\u003c/h2\u003e\n\u003cp\u003eWe got to cheat a little by applying a \u003ccode\u003emathlib\u003c/code\u003e theorem in the last example. This one will require a little more technique. First, let\u0026rsquo;s start by defining our set $B$.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003edef B : Set Int := {n | -4 ≤ n ∧ n ≤ 15 ∧ n % 2 = 0}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAs before, we can show that set membership in $B$ is decidable. We can make use of the fact that each condition within it is decidable through \u003ccode\u003eAnd.decidable\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003einstance (n : Int) : Decidable (n ∈ B) := by\n  suffices Decidable (-4 ≤ n ∧ n ≤ 15 ∧ n % 2 = 0) by\n    rewrite [B, Set.mem_setOf_eq]\n    assumption\n\n  exact And.decidable\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eSanity check to see that everything works as expected.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003e#eval List.Forall (· ∈ B) [-4, -2, 0, 2, 4, 6, 8, 10, 12, 14]\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe can also show that checking if a finset is a subset of $B$ is decidable.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003einstance (S : Finset Int) : Decidable (↑S ⊆ B) := by\n  suffices Decidable (∀ x ∈ S, -4 ≤ x ∧ x ≤ 15 ∧ x % 2 = 0) by\n    rewrite [Set.subset_def, B]\n    assumption\n  apply inferInstance\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe beginning of our proof stays the same\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003elemma instB : B = ({-4, -2, 0, 2, 4, 6, 8, 10, 12, 14} : Finset Int) := by\n  let S : Finset Int := {-4, -2, 0, 2, 4, 6, 8, 10, 12, 14}\n  change (B = ↑S)\n\n  -- To show equality, we need to show that\n  -- each is a subset of each other\n  suffices ((B ⊆ ↑S) ∧ (↑S ⊆ B)) by\n    rewrite [Set.Subset.antisymm_iff]\n    assumption\n \n  have H2 : ↑S ⊆ B := by decide\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFor the other direction we want to show that is $n$ meets the condition to be in $B$, then it must be in $S$.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003ehave H1 : B ⊆ ↑S := by\n  intro (n : ℤ)\n  intro (H : n ∈ B)\n\n  have H1 : -4 ≤ n ∧ n ≤ 15 ∧ n % 2 = 0 := by\n    rewrite [B] at H; assumption\n  clear H\n\n  show n ∈ S\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eSince $S$ is a finset, we can say that $n$ must be equal to one of the elements of $S$.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003erepeat rewrite [Finset.mem_insert]\nrewrite [Finset.mem_singleton]\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAt this point, the problem is integer arithmetic and we can call the \u003ccode\u003eomega\u003c/code\u003e tactic to finish the subproof.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003eomega\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWith both sides subset proven, we use and introduction to prove the goal\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003eexact show ((B ⊆ ↑S) ∧ (↑S ⊆ B)) from And.intro H1 H2\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe full proof is below.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003elemma instB : B = ({-4, -2, 0, 2, 4, 6, 8, 10, 12, 14} : Finset Int) := by\n  let S : Finset Int := {-4, -2, 0, 2, 4, 6, 8, 10, 12, 14}\n  change (B = ↑S)\n\n  -- To show equality, we need to show that\n  -- each is a subset of each other\n  suffices ((B ⊆ ↑S) ∧ (↑S ⊆ B)) by\n    rewrite [Set.Subset.antisymm_iff]\n    assumption\n\n  have H1 : B ⊆ ↑S := by\n    intro (n : ℤ)\n    intro (H : n ∈ B)\n\n    have H1 : -4 ≤ n ∧ n ≤ 15 ∧ n % 2 = 0 := by\n      rewrite [B] at H; assumption\n    clear H\n\n    show n ∈ S\n    repeat rewrite [Finset.mem_insert]\n    rewrite [Finset.mem_singleton]\n    omega\n\n  have H2 : ↑S ⊆ B := by decide\n\n  exact show ((B ⊆ ↑S) ∧ (↑S ⊆ B)) from And.intro H1 H2\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"example-3\"\u003eExample 3\u003c/h2\u003e\n\u003cp\u003eFinally let\u0026rsquo;s define our last set.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003edef C : Set Int := {n | n^2 = 6}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe\u0026rsquo;re aiming to show that $C$ is equivalent to the empty set. Therefore, we don\u0026rsquo;t have a need to show decidability for membership or finite subsets. We can\u0026rsquo;t make use of the mathlib theorem from Example 1, so how will we go about proving this?\u003c/p\u003e\n\u003cp\u003eAnalyzing the last example more carefully, we had upper and lower bounds to work with. We can similarly identify these bounds for this example.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s define the integer squared to be the upper bound.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003elemma IntPow2GeSelf (H : (a : Int)^2 = z) : a ≤ z := by\n  have H1 : a ≤ a ^ 2 := Int.le_self_sq a\n  rewrite [H] at H1\n  assumption\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFor the lower bound, take its negation.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003elemma NegIntPow2LeSelf (H : (a : Int)^2 = z) : a ≥ -z := by\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTo prove the lower bound, split up between positives and negatives using the law of excluded middle\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003ehave H1 : a ≥ 0 ∨ a \u0026lt; 0 := le_or_lt 0 a\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFor a positive $a$, we can rely on the \u003ccode\u003elinarith\u003c/code\u003e tactic\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003ehave H_LEFT : a ≥ 0 → a ≥ -z := by\n  have HL1 : a ≤ z := IntPow2GeSelf H\n  intro (HL2 : a ≥ 0)\n  linarith\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFor a negative $a$, we need to prove it by induction\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003ehave H_RIGHT : a \u0026lt; 0 → a ≥ -z := by\n  intro (HR1: a \u0026lt; 0)\n  have HR1 : a ≤ -1 := Int.le_sub_one_iff.mpr HR1\n  revert HR1\n\n  suffices a ≤ -1 → a ≥ -(a^2) by\n    rewrite [\u0026lt;- H]; assumption\n\n  let P : ℤ → Prop := fun x =\u0026gt; x ≥ -(x^2)\n\n  have H_base : P (-1) := by decide\n\n  have H_ind : ∀ (n : ℤ), n ≤ -1 → P n → P (n - 1) := by\n    intro (n : ℤ)\n    intro (H21 : n ≤ -1)\n    intro (H22 : P n)\n    simp_all\n    linarith\n\n  exact Int.le_induction_down H_base H_ind a\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe have shown that this lower bound holds for both positive and negative $a$, therefore we can show that it holds for all $a$.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003eexact Or.elim H1 H_LEFT H_RIGHT\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAll together the lower bound proof is the following\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003elemma NegIntPow2LeSelf (H : (a : Int)^2 = z) : a ≥ -z := by\n  have H1 : a ≥ 0 ∨ a \u0026lt; 0 := by omega\n\n  have H_LEFT : a ≥ 0 → a ≥ -z := by\n    have HL1 : a ≤ z := IntPow2GeSelf H\n    intro (HL2 : a ≥ 0)\n    linarith\n\n  have H_RIGHT : a \u0026lt; 0 → a ≥ -z := by\n    intro (HR1: a \u0026lt; 0)\n    have HR1 : a ≤ -1 := Int.le_sub_one_iff.mpr HR1\n    revert HR1\n\n    suffices a ≤ -1 → a ≥ -(a^2) by\n      rewrite [\u0026lt;- H]; assumption\n\n    let P : ℤ → Prop := fun x =\u0026gt; x ≥ -(x^2)\n\n    have H_base : P (-1) := by decide\n\n    have H_ind : ∀ (n : ℤ), n ≤ -1 → P n → P (n - 1) := by\n      intro (n : ℤ)\n      intro (H21 : n ≤ -1)\n      intro (H22 : P n)\n      simp_all\n      linarith\n\n    exact Int.le_induction_down H_base H_ind a\n\n\n  exact Or.elim H1 H_LEFT H_RIGHT\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWith our lower and upper bounds in place, we can look at the original problem. That is, we want to prove that $C = \\emptyset$. We\u0026rsquo;ll start the same proof like before.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003eexample : C = (∅ : Set Int) := by\n  suffices C ⊆ ∅ ∧ ∅ ⊆ C by\n    rewrite [Set.Subset.antisymm_iff]\n    assumption\n  have H2 : ∅ ⊆ C := Set.empty_subset C\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe start the other direction the same as well.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003ehave H1 : C ⊆ ∅ := by\n  intro (n : ℤ)\n  intro (H1_1 : n ∈ C)\n  have H1_1 : n^2 = 6 := by\n    rewrite [C, Set.mem_setOf_eq] at H1_1\n    assumption\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow bring in our bounds\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003ehave H1_2 : n ≤ 6 := by apply IntPow2GeSelf H1_1\nhave H2_3 : n ≥ -6 := by apply NegIntPow2LeSelf H1_1\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe can use \u003ccode\u003eomega\u003c/code\u003e to show that if the integer is within these bounds then $n$ must equal one of the integers.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003ehave H1_4 :  n ∈ ({-6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6} : Finset ℤ) := by\n  repeat rewrite [Finset.mem_insert]\n  rewrite [Finset.mem_singleton]\n  omega\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTurn this set into a disjunction\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003e-- Make H1_4 a disjunction (n = -6 ∨ ... ∨ n = 6)\nrepeat rewrite [Finset.mem_insert] at H1_4\nrewrite [Finset.mem_singleton] at H1_4\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTrying to show that $n$ is within the empty set is the same as trying to prove a contradiction\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003eshow False\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eSo let\u0026rsquo;s go through each disjunct in \u003ccode\u003eH1_4\u003c/code\u003e and show that we derive a contradiction.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003erepeat (\n  cases\u0026#39; H1_4 with H1_4H H1_4\n  -- Plug in n = ?? to n^2 = 6\n  rewrite [H1_4H] at H1_1\n  contradiction\n)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe last $n = 6$ is under a different name\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003erewrite [H1_4] at H1_1\ncontradiction\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe can finally put it all together with and introduction\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003eexact show C ⊆ ∅ ∧ ∅ ⊆ C from And.intro H1 H2\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAll together it\u0026rsquo;s the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003eexample : C = (∅ : Set Int) := by\n\n  suffices C ⊆ ∅ ∧ ∅ ⊆ C by\n    rewrite [Set.Subset.antisymm_iff]\n    assumption\n\n  have H1 : C ⊆ ∅ := by\n    intro (n : ℤ)\n    intro (H1_1 : n ∈ C)\n    have H1_1 : n^2 = 6 := by\n      rewrite [C, Set.mem_setOf_eq] at H1_1\n      assumption\n\n    show False\n\n    have H1_2 : n ≤ 6 := by apply IntPow2GeSelf H1_1\n    have H2_3 : n ≥ -6 := by apply NegIntPow2LeSelf H1_1\n\n\n    have H1_4 :  n ∈ ({-6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6} : Finset ℤ) := by\n      repeat rewrite [Finset.mem_insert]\n      rewrite [Finset.mem_singleton]\n      omega\n\n    -- Make H3 a disjunction (n = -6 ∨ ... ∨ n = 6)\n    repeat rewrite [Finset.mem_insert] at H1_4\n    rewrite [Finset.mem_singleton] at H1_4\n\n    -- Try each one, plug in n = a into n^2 = 6 and show it doesn\u0026#39;t work\n    repeat (\n      cases\u0026#39; H1_4 with H1_4H H1_4\n      rewrite [H1_4H] at H1_1\n      contradiction\n    )\n\n    -- Last n = 6 has a different name\n    rewrite [H1_4] at H1_1\n    contradiction\n\n\n  have H2 : ∅ ⊆ C := Set.empty_subset C\n\n  exact show C ⊆ ∅ ∧ ∅ ⊆ C from And.intro H1 H2\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eWe gave three examples on working with finite integer sets. The two key lessons from this post are:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e1. If the set is equal to a non-empty finite set, then try to prove decidability of membership and if a finset is a subset of it.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eIf you\u0026rsquo;re looking at a set of integers and this set is constructed with conditions that are mostly built-in (such as the \u003ccode\u003e\u0026lt;\u003c/code\u003e relation), then this is hopefully not too difficult. Unfold the definitions of your set using \u003ccode\u003erewrite\u003c/code\u003e and have Lean auto-infer which decidability instance to call using \u003ccode\u003eapply inferInstance\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Try to establish a lower and upper bound for the elements of your set.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThis gives the \u003ccode\u003eomega\u003c/code\u003e tactic something additional to work with. In the second example, \u003ccode\u003eomega\u003c/code\u003e was able to close out the goal completely given the linear inequalities presented. In our last example, we only used \u003ccode\u003eomega\u003c/code\u003e to construct a finite set of possible integers.\u003c/p\u003e\n\u003cp\u003eIf you develop any other general techniques for dealing with integer sets let me know. Otherwise, feel free to get in touch with any questions you have.\u003c/p\u003e\n",
    "date_published": "2024.03.31",
    "tags": ["Formal Methods"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/induction-ints-coercing-nats-lean4/",
    "url": "https://brandonrozek.com/blog/induction-ints-coercing-nats-lean4/",
    "title": "Coercing Ints to Nats for Induction in Lean 4",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWarning: This post is what happens when I solve a problem that\u0026rsquo;s already solved within Mathlib. I find it interesting enough to share though. Hopefully this will inspire techniques to use within your own proofs.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eBefore getting into how to do induction on integers through nat coercion, let\u0026rsquo;s discuss the proper way of solving this problem using \u003ccode\u003emathlib\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eLet $x$ be an integer greater than some integer $m$. Show that $x$ satisfies some property. For example, prove $x \u0026gt; -5 \\rightarrow x \u0026gt; -10$.\u003c/p\u003e\n\u003cp\u003eOkay, I hear you, \u003ccode\u003elinarith\u003c/code\u003e can do this for us. Let\u0026rsquo;s pretend that doesn\u0026rsquo;t exist for now and show how we can go about solving this using \u003ccode\u003emathlib\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003eexample (x : ℤ):  x \u0026gt; -5 → x \u0026gt; -10 := by\n  -- Int.le_induction expects something of the form m ≤ x → ...\n  show -4 ≤ x → x \u0026gt; -10\n\t\n  -- Int.le_induction doesn\u0026#39;t let you specify the\n  -- motive so you need to make it easy for it to induce\n  let P : ℤ → Prop := fun x =\u0026gt; x \u0026gt; -10\n\t\n  -- Base Case\n  have H_base : P (-4) := by decide\n\t\n  -- Inductive case\n  have H_ind : ∀ (n : ℤ), -4 ≤ n → P n → P (n + 1) := by\n    intro (n : ℤ)\n    intro (HH1 : -4 ≤ n)\n    intro (HH2 : n \u0026gt; -10)\n    show n + 1 \u0026gt; -10\n    exact lt_add_of_lt_of_pos HH2 (show 0 \u0026lt; 1 by decide)\n   \n -- Use the induction principle\n exact show (-4 ≤ x → x \u0026gt; -10) from Int.le_induction H_base H_ind x\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003ccode\u003eInt.le_induction\u003c/code\u003e is a nice and clean solution which I highly recommend you use. It makes sense too, prove that the property works for the lower bound and then prove the induction case holds.\u003c/p\u003e\n\u003cp\u003eNow what if for some reason you couldn\u0026rsquo;t rely \u003ccode\u003eInt.le_induction\u003c/code\u003e and you wanted to find another way to go about this? This is where coercion comes in.\u003c/p\u003e\n\u003cp\u003eStarting from the beginning of the proof, this time let\u0026rsquo;s introduce the hypothesis.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eintro (H : x \u0026gt; -5)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow we want to create a natural number which we\u0026rsquo;ll perform induction over. Similar to the mathlib proof, we want to start with the lower bound as the base case. Therefore, make it so when $x$ is the lower bound our new natural number variable $n$ is 0.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003elet n : ℕ := Int.toNat (x + 5)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eOur goal \u003ccode\u003ex \u0026gt; -10\u003c/code\u003e is still written in terms of the original variable $x$. To rewrite this, we\u0026rsquo;ll need to prove the following relationship:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003ehave H1 : x = n - 5 := by\n  -- We should start off by saying this relationship\n  -- holds for the integer version of n\n  let nz : ℤ := x + 5\n  have H1_1 : x = nz - 5 := eq_sub_of_add_eq (show nz = x + 5 by rfl)\n  suffices nz - 5 = n - 5 by rewrite [H1_1]; assumption\n\n  -- Through congruence closure we can ignore the (- 5)\n  suffices nz = n by\n    let Minus5 : ℤ → ℤ := fun x =\u0026gt; x - 5\n    show Minus5 nz = Minus5 n\n    exact congrArg Minus5 this\n\n  -- An integer coercion is only equal to a nat\n  -- if the integer was 0 or positive to begin with\n  have H1_2 : nz ≥ 0 := by\n    have H : nz - 5 \u0026gt; 0 - 5 := by rewrite [\u0026lt;- H1_1]; assumption\n    have H1 : nz - 5 ≥ 0 - 5 := Int.le_of_lt H\n    exact (Int.add_le_add_iff_right (-5)).mp H1\n\n  exact show nz = n from (Int.toNat_of_nonneg H1_2).symm\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWith $x$ written in terms of $n$, we can now rewrite our goal using our natural number.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003esuffices n - 5 \u0026gt; (-10: ℤ) by rewrite [H1]; assumption\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eHere\u0026rsquo;s the rest of the induction. Of course like before, I avoid  the use of \u003ccode\u003elinarith\u003c/code\u003e so I wouldn\u0026rsquo;t be cheating ;)\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean4\" data-lang=\"lean4\"\u003ehave H_base : 0 - 5 \u0026gt; -10 := by decide\n\nhave H_ind : ∀ n : ℕ, n - 5 \u0026gt; (-10 : ℤ) → (n + 1) - 5 \u0026gt; (-10: ℤ) := by\n  intro n\n  intro (IH : n - 5 \u0026gt; (-10 : ℤ))\n  have IH : (-10: ℤ) \u0026lt; n - 5 := IH\n  show (-10: ℤ) \u0026lt; n + 1 - 5\n\n  have IH_2 : n + 1 - (5: ℤ) = n + (-5 : ℤ) + 1 := by calc\n    n + 1 - (5: ℤ) = n + 1 + (-5 : ℤ) := rfl\n    _ = n + (1 + (-5: ℤ))  := Int.add_assoc (↑n) 1 (-5)\n    _ = n + ((-5 : ℤ) + 1) := rfl\n    _ = n + (-5 : ℤ) + 1 := (Int.add_assoc (↑n) (-5) 1).symm\n    _ = n - (5: ℤ) + 1 := rfl\n\n  suffices -10 \u0026lt; n - (5: ℤ) + 1 by rewrite [IH_2]; assumption\n\n  exact lt_add_of_lt_of_pos IH (show 0 \u0026lt; 1 by decide)\n\nexact show n - 5 \u0026gt; (-10: ℤ) from Nat.recOn\n  (motive := fun x: ℕ =\u0026gt; x - (5 : ℤ) \u0026gt; (-10: ℤ))\n  n\n  H_base\n  H_ind\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis version is slightly longer than the \u003ccode\u003eInt.le_induction\u003c/code\u003e version as we had to carry forward the \u003ccode\u003e- 5\u003c/code\u003e portion of the equations. While it might not make sense in the integer case to perform this coercion, I\u0026rsquo;m hopeful that I can use a technique similar to this on other inductive types in the future.\u003c/p\u003e\n\u003cp\u003eLet me know if you end up using this or if you have any other cool induction techniques in your tool belt. Until next time.\u003c/p\u003e\n",
    "date_published": "2024.03.27",
    "tags": ["Formal Methods"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/lean4-tutorial/",
    "url": "https://brandonrozek.com/blog/lean4-tutorial/",
    "title": "Lean 4 Tutorial",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLean is an interactive theorem prover created by Microsoft.\nIn 2021, they performed a major transition from Lean 3 to Lean 4.\nThis transition changed the syntax and tactics used within proofs\nand caused major library developers such as Mathlib to transition.\u003c/p\u003e\n\u003cp\u003eLast year I wrote a \u003ca href=\"/blog/lean3-tutorial\"\u003eLean 3 tutorial\u003c/a\u003e which showcases\nhow to go about constructing proofs for several small problems.\nAfter revisiting Lean 4 recently,\nI decided that Mathlib support is comprehensive and stable enough to make\nthe switch.\u003c/p\u003e\n\u003cp\u003eThis post is the same as the Lean 3 tutorial that I wrote last year, except\nthat the examples have been updated to the latest syntax.\u003c/p\u003e\n\u003ch2 id=\"propositional-logic-intuitionist-fragment\"\u003ePropositional Logic (Intuitionist Fragment)\u003c/h2\u003e\n\u003cp\u003eTo prove something in Lean, we need to construct an object of that type.\u003c/p\u003e\n\u003ch3 id=\"conjunctive-introduction\"\u003eConjunctive Introduction\u003c/h3\u003e\n\u003cp\u003eTo start off let us prove the statement $P \\wedge Q$ given both $P$ and $Q$ individually.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_p : p) (H_q : q) : (p ∧ q) := by\n  exact show p ∧ q from And.intro H_p H_q\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis shows an application of conjunctive introduction. We created the object $P \\wedge Q$ by applying the theorem \u003ccode\u003eAnd.intro\u003c/code\u003e to both hypotheses \u003ccode\u003eH_p\u003c/code\u003e which contains $P$ and \u003ccode\u003eH_q\u003c/code\u003e which contains $Q$.\u003c/p\u003e\n\u003cp\u003eAnother way of going about this proof is by \u003cem\u003etransforming the goal to another equivalent one\u003c/em\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_p : p) (H_q : q) : (p ∧ q) := by\n  constructor\n  case left =\u0026gt;\n    exact show p from H_p\n  case right =\u0026gt;\n    exact show q from H_q\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIn this proof, we make use of the \u003ccode\u003econstructor\u003c/code\u003e tactic. Given a conjunctive goal $P \\wedge Q$, this tactic replaces it with two subgoals $P$ and $Q$. We can then apply the hypotheses to solve the problem.\u003c/p\u003e\n\u003ch3 id=\"conjunctive-elimination\"\u003eConjunctive Elimination\u003c/h3\u003e\n\u003cp\u003eGiven the proof of $P \\wedge Q$, we can derive $P$ from it.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_pq : p ∧ q) : p := by\n  exact show p from And.left H_pq\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIn the example above, we apply the inference \u003ccode\u003eAnd.left\u003c/code\u003e to be able to derive the left side of a conjunct. Similarly to the last inference rule, there is a tactic based way to go about it. Tactic based methods are generally more prevalent within the ITP community.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_pq : p ∧ q) : p := by\n  cases\u0026#39; H_pq with H_p H_q -- p, q\n  exact show p from H_p\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eGiven a conjunctive hypothesis, the \u003ccode\u003ecases'\u003c/code\u003e tactic will create two new hypothesis from it. The two labels after the \u003ccode\u003ewith\u003c/code\u003e is to specify the names of these hypotheses. The string \u003ccode\u003e--\u003c/code\u003e denotes the start of a comment; I left a small comment to remind me the ordering of the conjuncts.\u003c/p\u003e\n\u003ch3 id=\"disjunctive-introduction\"\u003eDisjunctive Introduction\u003c/h3\u003e\n\u003cp\u003eGiven any statement $P$, we can introduce an arbitrary formula as a disjunct. More intuitively, if Jarold is above the age of 10, then he is above the age of 10 or he likes lollipops. It does not change the truth value of the statement.\u003c/p\u003e\n\u003cp\u003eThe inference based way of representing this is as follows:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_p : p) : (p ∨ q) := by\n  exact show p ∨ q from Or.intro_left q H_p\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIn the more popular tactic based notion. We use the tactic \u003ccode\u003eleft\u003c/code\u003e or \u003ccode\u003eright\u003c/code\u003e to denote which side we\u0026rsquo;re going to attempt to prove. If we\u0026rsquo;re trying to prove $P \\vee Q$ and we have $P$ then we will prove the left side.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_p : p) : (p ∨ q) := by\n  left\n  exact show p from H_p\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"disjunctive-elimination\"\u003eDisjunctive Elimination\u003c/h3\u003e\n\u003cp\u003eThe form of disjunctive elimination included in Lean is more commonly known as \u003cem\u003eproof by cases\u003c/em\u003e. That is if you know either $A$ or $B$ is true. That is, $A \\vee B$. And, you can derive $C$ from $A$ as well as $C$ from $B$. Then it doesn\u0026rsquo;t matter which of $A$ or $B$ is true, because you can derive $C$ regardless.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://en.wikipedia.org/wiki/Disjunction_elimination\"\u003eWikipedia\u003c/a\u003e has a nice intuitive example:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eIf I\u0026rsquo;m inside, I have my wallet on me.\u003c/p\u003e\n\u003cp\u003eIf I\u0026rsquo;m outside, I have my wallet on me.\u003c/p\u003e\n\u003cp\u003eIt is true that either I\u0026rsquo;m inside or I\u0026rsquo;m outside.\u003c/p\u003e\n\u003cp\u003eTherefore, I have my wallet on me.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eTo achieve this in Lean using the inference based approach.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q r : Prop} (H_pr : p → r) (H_qr : q → r) (H_pq : p ∨ q) : r := by\n  exact show r from Or.elim H_pq H_pr H_qr\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAlternatively, via the tactic based approach\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q r : Prop} (H_pr : p → r) (H_qr : q → r) (H_pq : p ∨ q) : r := by\n  cases\u0026#39; H_pq with H_p H_q\n  case inl =\u0026gt;\n    -- Assume p, apply modus ponens with H_pr\n    exact show r from H_pr H_p\n  case inr =\u0026gt;\n    -- Assume q, apply modus ponens with H_qr\n    exact show r from H_qr H_q\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"negation\"\u003eNegation\u003c/h2\u003e\n\u003cp\u003eTraditional interactive theorem provers such as Coq focused on the constructivist approach to theorem proving.\nThis allows them to export proofs as programs in OCaml.\nLean places less of an emphasis on this approach and instead supports the proof by contradiction style\nyou see in classical theorem proving.\u003c/p\u003e\n\u003cp\u003eFor this tutorial, I decided to make this distinction\nexplicit by declaring a new axiom for the law of\nexcluded middle.\nBy default, Lean has the axiom of choice which\nthey use to prove the law of excluded middle\nthrough the proof \u003ccode\u003eClassical.em\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eIn other words, if you want to perform a proof by contradiction, don\u0026rsquo;t use the techniques shown in this section and instead use the \u003ccode\u003eby_contra\u003c/code\u003e tactic or directly use \u003ccode\u003eClassical.em\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eDeclare the axiom of law of excluded middle:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eaxiom LEM {p : Prop}: p ∨ ¬ p\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"negation-introduction\"\u003eNegation Introduction\u003c/h3\u003e\n\u003cp\u003eFor negation introduction, let\u0026rsquo;s say we have some proposition $P$. If we can use $P$ to derive a falsity, let\u0026rsquo;s say $Q \\wedge \\neg Q$ then $P$ must be false. That is, we introduce a negation to make it $\\neg P$.\u003c/p\u003e\n\u003cp\u003eThe structure of the proof will a proof by cases on the law of exclude middle.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003elemma negation_intro {p q : Prop} (H_pq : p → q) (H_pnq : p → ¬q) : ¬p := by\n  have H_LEM : p ∨ ¬p := LEM\n\n  -- Assuming ¬p derive ¬p\n  have H_npp : ¬p → ¬p := by\n    intro (H_np : ¬p)\n    exact show ¬p from H_np\n\n  -- Assuming p derive ¬p\n  have H_pp : p → ¬p := by\n    -- Use hypotheses to obtain q and ¬q\n    intro (H_p : p)\n    have H_q : q := H_pq H_p\n    have H_nq : ¬q := H_pnq H_p\n\n    -- We can derive falsity from a direct contradiction\n    have H_false : False := H_nq H_q\n\n    -- You can derive anything from false\n    have H_UNUSED: p ∧ ¬p := False.elim H_false\n\n    -- Including what we want, ¬p\n    exact show ¬p from False.elim H_false\n\n  -- By proof by cases, we derive ¬p\n  exact show ¬p from Or.elim H_LEM H_pp H_npp\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eLean can tell us which axioms our proof depends on as well.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003e#print axioms negation_intro\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis returns \u003ccode\u003e'negation_intro' depends on axioms: [LEM]\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eAlternatively, the tactic based approach\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_pq : p → q) (H_pnq : p → ¬q) : ¬p := by\n  have H_LEM : p ∨ ¬p := LEM\n  cases\u0026#39; H_LEM with H_p H_np\n\n  case inl =\u0026gt;\n    have H_q : q := H_pq H_p\n    have H_nq : ¬q := H_pnq H_p\n    have H_false : False := H_nq H_q\n    exact show ¬p from False.elim H_false\n\n  case inr =\u0026gt;\n    exact show ¬p from H_np\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAs I mentioned before, we do not have to explicitly declare the\naxiom of excluded middle. Instead we can make use of Lean\u0026rsquo;s builtin\nclassical reasoning.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_pq : p → q) (H_pnq : p → ¬q) : ¬p := by\n  by_contra H_p\n  have H_q : q := H_pq H_p\n  have H_nq : ¬q := H_pnq H_p\n  exact show False from H_nq H_q\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"double-negation-elimination\"\u003eDouble Negation Elimination\u003c/h3\u003e\n\u003cp\u003eOne common representation of negation elimination is to remove any double negations.\nThat is $\\neg \\neg P$ becomes $P$.\u003c/p\u003e\n\u003cp\u003eWe\u0026rsquo;ll similarly show this by performing a proof by cases on the law of excluded middle.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p: Prop} (H_nnp : ¬¬p) : p := by\n  have H_LEM : p ∨ ¬p := LEM\n\n  -- Assuming ¬p derive p\n  have H_np2p : ¬p → p := by\n    intro (H_np : ¬p)\n    -- ¬p and ¬¬p are a direct contradiction\n    have H_false : False := H_nnp H_np\n    -- Derive our goal from a falsity\n    exact show p from False.elim H_false\n\n  -- Assuming p derive p\n  have H_p2p : p → p := by\n    intro (H_p : p)\n    exact show p from H_p\n\n  -- By proof by cases, we derive p\n  exact show p from Or.elim H_LEM H_p2p H_np2p\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAlternatively for the tactic based approach.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p: Prop} (H_nnp : ¬¬p) : p := by\n  have H_LEM : p ∨ ¬p := LEM\n  cases\u0026#39; H_LEM with H_p H_np\n\n  -- Assuming p, derive p\n  case inl =\u0026gt;\n    exact show p from H_p\n\n  -- Assuming ¬p derive p\n  case inr =\u0026gt;\n    -- ¬p and ¬¬p are a direct contradiction\n    have H_false : False := H_nnp H_np\n    exact show p from False.elim H_false\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eLean has this theorem built-in with \u003ccode\u003eClassical.not_not\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"first-order\"\u003eFirst Order\u003c/h2\u003e\n\u003cp\u003eLean is also capable of reasoning over first order logic. In this section, we\u0026rsquo;ll start seeing objects/terms and predicates instead of just propositions.\u003c/p\u003e\n\u003cp\u003eFor example \u003ccode\u003e{α : Type} {P : α → Prop} \u003c/code\u003e means that $P$ is a predicate of arity one and takes an object of type $\\alpha$.\u003c/p\u003e\n\u003ch3 id=\"forall-elim\"\u003eForall Elim\u003c/h3\u003e\n\u003cp\u003eIf we have a forall statement, then we can replace the bound variable with an object of that type and remove the forall. Lets say for our example we have the following forall statement: $\\forall x \\in \\mathbb{N}: x \\ge 0$. Then we can replace the $x$ with $2$ and get the following formula: $2 \\ge 0$.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {α : Type} {P : α → Prop} {y : α} (H : ∀ x : α,  P x) : P y := by\n  exact show P y from H y\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"forall-intro\"\u003eForall Intro\u003c/h3\u003e\n\u003cp\u003eTo show that some property holds for all $x$ of a type α, you need to show that it holds\nfor an arbitrary $x$ of that type. We can introduce this object, by the \u003ccode\u003eintro\u003c/code\u003e command.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {α : Type} {P Q R : α → Prop} (H_pq : ∀ x : α, P x → Q x) (H_qr : ∀ x : α, Q x → R x) : ∀ x : α, P x → R x := by\n  intro (y: α)\n  have H_pqx : P y → Q y := H_pq y\n  have H_qrx : Q y → R y := H_qr y\n  intro (H_py : P y)\n  have H_qy : Q y := H_pqx H_py\n  exact show R y from H_qrx H_qy\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"exists-intro\"\u003eExists Intro\u003c/h3\u003e\n\u003cp\u003eTo introduce an existential, you need to show that the formula holds for any object of a certain type.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {α : Type} {P : α → Prop} {y : α} (H: P y) : ∃ x: α, P x := by\n  exact show ∃ x, P x from Exists.intro y H\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIn the tactic based approach, this is done via \u003ccode\u003eexists\u003c/code\u003e:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {α : Type} {P : α → Prop} {y : α} (H: P y) : ∃ x: α, P x := by\n  exact show ∃ x, P x from by exists y\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"exists-elim\"\u003eExists Elim\u003c/h3\u003e\n\u003cp\u003eLets say we have the following forall statement: $\\forall a \\in \\alpha: p a \\implies b$.\u003c/p\u003e\n\u003cp\u003eNow lets say we have the following existential: $\\exists x, p x$.\u003c/p\u003e\n\u003cp\u003eUsing these, we can derive $b$.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {α : Type} {p : α → Prop} {b : Prop} (H_epx : ∃ x, p x) (H_pab : ∀ (a : α), p a → b) : b := by\n  exact show b from Exists.elim H_epx H_pab\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAlternatively for the tactic based approach:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {α : Type} {p : α → Prop} {b : Prop} (H_epx : ∃ x, p x) (H_pab : ∀ (a : α), p a → b) : b := by\n  cases\u0026#39; H_epx with c H_pc\n  have H_pcb : p c → b := H_pab c\n  exact show b from H_pcb H_pc\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"inductive-types\"\u003eInductive Types\u003c/h2\u003e\n\u003cp\u003eOne of the biggest use cases of an interactive\ntheorem prover is in program verification.\nTo help represent recursive data structures, we\nhave the notion of an \u003cem\u003einductive type\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s create a custom representation of a list.\nA list can either by empty (\u003ccode\u003ecnil\u003c/code\u003e) or be an element \u003ccode\u003ehd\u003c/code\u003e combined with the rest of some list \u003ccode\u003etl\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003einductive CustomList (T : Type) where\n| cnil : CustomList T\n| ccons (hd : T) -\u0026gt; (tl : CustomList T) : CustomList T\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eSome examples of a list here include \u003ccode\u003ecnil\u003c/code\u003e,\n\u003ccode\u003eccons(0, cnil)\u003c/code\u003e, and \u003ccode\u003eccons(1, ccons(0, cnil))\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFor convenience, we\u0026rsquo;ll open the \u003ccode\u003eCustomList\u003c/code\u003e namespace\nso that we don\u0026rsquo;t have to refer each constructor\n(\u003ccode\u003ecnil\u003c/code\u003e/\u003ccode\u003eccons\u003c/code\u003e) by it.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eopen CustomList\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"functions-over-inductive-types\"\u003eFunctions over Inductive Types\u003c/h3\u003e\n\u003cp\u003eTo define a function over an inductive type,\nwe need to cover each of the constructors.\u003c/p\u003e\n\u003cp\u003eFor example, let\u0026rsquo;s consider the notion of a list\u0026rsquo;s length.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eIf the list is \u003ccode\u003ecnil\u003c/code\u003e then the length is $0$.\u003c/li\u003e\n\u003cli\u003eIf the list starts with \u003ccode\u003eccons\u003c/code\u003e then we add 1 to the length of the tail \u003ccode\u003etl\u003c/code\u003e.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003edef clength {α : Type}:  CustomList α → Nat\n  | cnil =\u0026gt; 0\n  | (ccons _ as) =\u0026gt; 1 + clength as\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe can see the output of a function via the \u003ccode\u003e#eval\u003c/code\u003e command.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003e#eval clength (@cnil Nat)\n#eval clength (ccons 2 (ccons 1 cnil))\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFor another example, let us look at appending two lists. As an example if we have the list \u003ccode\u003e[1, 2, 3]\u003c/code\u003e and the list \u003ccode\u003e[4, 5, 6]\u003c/code\u003e, then appending those two lists will create \u003ccode\u003e[1, 2, 3, 4, 5, 6]\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003edef cappend {α : Type} (as bs : CustomList α) : CustomList α :=\n  match as with\n  | cnil =\u0026gt; bs\n  | ccons a as =\u0026gt; ccons a (cappend as bs)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eExample evaluations, make sure these come out to what you expect.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003e#eval cappend (ccons 1 cnil) (ccons 2 cnil)\n#eval clength (cappend (ccons 1 cnil) (ccons 2 cnil))\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"theorems-over-inductive-types\"\u003eTheorems over Inductive Types\u003c/h3\u003e\n\u003cp\u003eNow that we have a data structure and some methods over it. We can now prove some interesting properties.\u003c/p\u003e\n\u003cp\u003eFirst let\u0026rsquo;s start off with the following theorem.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eAppending \u003ccode\u003ecnil\u003c/code\u003e to a list \u003ccode\u003eas\u003c/code\u003e is equivalent to the list \u003ccode\u003eas\u003c/code\u003e.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eWhen instantiating an inductive type, the inductive hypothesis is created for it via \u003ccode\u003erecOn\u003c/code\u003e.\nTherefore, we can rely on that for the proof.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem append_nil {α : Type} (as : CustomList α) : cappend as cnil = as := by\n  -- Base Case\n  have H_BASE : (cappend cnil cnil = (@cnil α)) := by rfl\n\n  -- Inductive Step\n  have H_Inducive : ∀ (hd : α) (tl : CustomList α), cappend tl cnil = tl → cappend (ccons hd tl) cnil = ccons hd tl := by\n    intro (hd : α)\n    intro (tl : CustomList α)\n    intro (IH : cappend tl cnil = tl)\n    calc\n        cappend (ccons hd tl) cnil = ccons hd (cappend tl cnil) := by rw [cappend]\n        _ = ccons hd tl := by rw [IH]\n\n  -- Apply induction principle\n  exact CustomList.recOn  (motive := fun x =\u0026gt; cappend x cnil = x)\n    as\n    H_BASE\n    H_Inducive\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe \u003ccode\u003erewrite\u003c/code\u003e/\u003ccode\u003erw\u003c/code\u003e command allows us to replace instances of functions with their definitions. The goal is to get both sides of the equality to be syntactically the same.\nThe \u003ccode\u003ecalc\u003c/code\u003e environment allows us to perform multiple rewrites in order to get closer to that end.\u003c/p\u003e\n\u003cp\u003eInstead of explicitly making use of \u003ccode\u003erecOn\u003c/code\u003e, we can use the \u003ccode\u003einduction\u003c/code\u003e tactic.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem append_nil2 {α : Type} (as : CustomList α) : cappend as cnil = as := by\n  induction as\n\n  case cnil =\u0026gt;\n    calc\n      cappend cnil cnil = cnil := by rw [cappend]\n      _ = cnil := by exact rfl\n\n  case ccons hd tl IH =\u0026gt;\n    calc\n      cappend (ccons hd tl) cnil = ccons hd (cappend tl cnil) := by rw [cappend]\n      _ = ccons hd tl := by rw [IH]\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"double-induction\"\u003eDouble Induction\u003c/h3\u003e\n\u003cp\u003eFor our next example, we\u0026rsquo;ll need to perform induction on two lists.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eGiven two lists \u003ccode\u003eas\u003c/code\u003e and \u003ccode\u003ebs\u003c/code\u003e. The length of their append is the same as the length of each individual list added together.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem length_append_sum {α : Type} (as bs : CustomList α) : clength (cappend as bs) = clength as + clength bs := by\n  induction as\n  induction bs\n\n  case cnil.cnil =\u0026gt;\n    calc\n      clength (cappend cnil cnil) = clength cnil := by rw [cappend]\n      _ = 0 := by rw [clength]\n      _ = 0 + 0 := by linarith\n      _ = (clength cnil) + (clength cnil) := by rw [clength]\n\n  case cnil.ccons hd tl _ =\u0026gt;\n    calc\n      clength (cappend cnil (ccons hd tl)) = clength (ccons hd tl) := by rw [cappend]\n      _ = 0 + clength (ccons hd tl) := by linarith\n      _ = clength cnil + clength (ccons hd tl) := by rw [clength]\n\n  case ccons hd tl IH =\u0026gt;\n    calc\n      clength (cappend (ccons hd tl) bs) = clength (ccons hd (cappend tl bs)) := by rw [cappend]\n      _ = 1 + clength (cappend tl bs) := by rw [clength]\n      _ = 1 + (clength tl + clength bs) := by rw [IH]\n      _ = (1 + clength tl) + clength bs := by linarith\n      _ = clength (ccons hd tl) + clength bs := by rw [clength]\n\n  -- Lean intelligently wlogs the fourth case\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNotice that a couple times in the proof, make use of a tactic called \u003ccode\u003elinarith\u003c/code\u003e. This stands for \u0026ldquo;linear arithmetic\u0026rdquo; and helps solve goals involving numbers. I generally employ this rather than figuring out which definitions I need to perform commutativity and associativity rules.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eOne of my favorite things about Lean is the ability to make proofs\nmore readable by making use of the \u003ccode\u003ehave\u003c/code\u003e, \u003ccode\u003ecalc\u003c/code\u003e, and \u003ccode\u003erewrite\u003c/code\u003e\ntactics. I do find it easier, however, to use the \u003ccode\u003einduction\u003c/code\u003e tactic\nfor inductive proofs. Especially when dealing with nested\ninductions as writing out the cases explicitly can be daunting.\u003c/p\u003e\n\u003cp\u003eIf you catch any mistakes in me converting this post, let me know.\nOtherwise feel free to email me if you have any questions.\u003c/p\u003e\n\u003cp\u003eLastly, I want to give my thanks to James Oswald for helping proofread\nthis post and making it better.\u003c/p\u003e\n",
    "date_published": "2024.03.17",
    "tags": ["Formal Methods"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/caching-theorem-prover-results/",
    "url": "https://brandonrozek.com/blog/caching-theorem-prover-results/",
    "title": "Caching Theorem Prover Results",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://github.com/rairlab/spectra\"\u003eSpectra\u003c/a\u003e is an automated planner that uses theorem proving to decide which actions to try out and determine when the goal is reached. Given an execution of Spectra on a problem within a domain, it\u0026rsquo;s current architecture involves making many calls of similar structure to a theorem prover.\u003c/p\u003e\n\u003cp\u003eFor example we know that $\\{ P, P \\rightarrow Q \\} \\vdash Q$ holds. In classical first order logic, it is also the case that $\\{ P, P \\rightarrow Q, R\\} \\vdash Q$ holds.\u003c/p\u003e\n\u003cp\u003eThat is to say, adding additional formulae does not affect the result. In fact in classical first order logic, adding contradictory formulae also does not affect the provability of a given statement. This is due to the principle of explosion which states that from a contradiction we can derive anything.\u003c/p\u003e\n\u003cp\u003eWhat can we say about the other direction? How do we know when something does not hold? Let\u0026rsquo;s say we know the following statement: $\\{ R, Z, P \\rightarrow Q \\} \\not\\vdash Q$. What can we say about $\\{ Z, P \\rightarrow Q \\} \\not\\vdash Q$?\u003c/p\u003e\n\u003cp\u003eIntuitively, if we cannot show a formula $Q$  given some set of information. Then in classical first order logic, it goes to show that we cannot show $Q$ with less information.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s discuss how this works in practice. Let $P$ be the set of assumptions which prove a corresponding goal. That is, given $(\\Gamma_i, G_i) \\in P, \\Gamma_i \\vdash G_i$. On the other hand, let $N$ be the set of assumptions which don\u0026rsquo;t prove a corresponding goal, i.e. $(\\Gamma_i, G_i) \\in N, \\Gamma_i \\not\\vdash G_i$.\u003c/p\u003e\n\u003cp\u003eGiven a problem $\\Gamma \\vdash G$. We perform two caching checks before calling the full automated theorem prover.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eIf there exists a $(\\Gamma_i, G_i) \\in P$ such that $G = G_i$ and $\\Gamma_i \\subseteq \\Gamma$, then we know that $\\Gamma \\vdash G$ holds.\u003c/li\u003e\n\u003cli\u003eOn the other hand, if there exists a $(\\Gamma_i, G_i) \\in N$ such that $G = G_i$ and $\\Gamma \\subseteq \\Gamma_i$, then we know that $\\Gamma \\not\\vdash G$.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eIf either of those two conditions don\u0026rsquo;t get matched, then we can call the full automated theorem prover to determine the result and cache it in either $P$ or $N$.\u003c/p\u003e\n\u003cp\u003eThis caching technique might not work well in your application. It works well in Spectra since we\u0026rsquo;re trying to prove a small set of goals and action preconditions and the assumptions consist of state variables which don\u0026rsquo;t vary by much between steps.\u003c/p\u003e\n\u003cp\u003eThough keep in mind that this technique will give you flawed answers if the corresponding logic is non-monotonic. Classical first-order and propositional logic is monotonic, however, so this caching technique is safe to use in those settings.\u003c/p\u003e\n\u003cp\u003eIn non-monotonic or defeasible logic, you could have a statement $B^2(\\neg P)$ which defeats another statement $B^1(P)$. We can read the last example as \u0026ldquo;I have a strong belief that P does not holds and a weak belief that P holds\u0026rdquo;. This depending on the defeasible logic, can change whether of not given $B^\\sigma(P \\rightarrow Q)$, if $B^{\\sigma_i}(Q)$ holds.\u003c/p\u003e\n",
    "date_published": "2024.03.02",
    "tags": ["Logic"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/svg-pixel-art/",
    "url": "https://brandonrozek.com/blog/svg-pixel-art/",
    "title": "SVG Pixel Art",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis year, I participated in the 32-bit Cafe \u003ca href=\"https://tilde.32bit.cafe/~ribose/events/valentines2024/\"\u003eValentine\u0026rsquo;s Day Event\u003c/a\u003e. They gave a few ways to participate, and this year I made a little Valentine\u0026rsquo;s day card.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20240219105747.png\" alt=\"Image of card that says \u0026ldquo;Happy Valentine\u0026rsquo;s\u0026rdquo; with pixel art of a heart.\"\u003e\u003c/p\u003e\n\u003cp\u003eFor the whole animated version, check out my \u003ca href=\"http://tilde.club/~brozek/valentines-2024.html\"\u003etilde.club page\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThe picture above is a PNG screenshot, but for the card itself I wanted to use SVGs as the primary graphics format to create the heart.\u003c/p\u003e\n\u003cp\u003eIf you were to ask me why, I would claim it\u0026rsquo;s because SVGs are flexible when it comes to size. Though if I was to be honest, it\u0026rsquo;s beause SVGs are a plaintext format that I can edit.\u003c/p\u003e\n\u003cp\u003eLooking around for a tool to create this, I came across a \u003ca href=\"https://jerosoler.github.io/SvgPixelArt/\"\u003eSVG Pixel Art project\u003c/a\u003e from Jero Soler.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20240219110500.png\" alt=\"Screenshot of the pixel art editor\"\u003e\u003c/p\u003e\n\u003cp\u003eThe editor provides a very familiar experience to Gimp and Photoshop. It includes advance features like layering which I didn\u0026rsquo;t feel the need to use for this quick drawing.\u003c/p\u003e\n\u003cp\u003eThe available colors leave some to be desired. After downloading the SVG, I was able to edit the \u003ccode\u003efill\u003c/code\u003e attribute with the hex values of the tacky colors I wanted to use instead.\u003c/p\u003e\n\u003cp\u003eI recommend trying the website out yourself next time you want to make some pixel art. For me, it\u0026rsquo;s less daunting and more familiar to use than Inkscape, the more prevalent svg editing tool.\u003c/p\u003e\n",
    "date_published": "2024.02.19",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/python-refactoring-exceptions-context-manager/",
    "url": "https://brandonrozek.com/blog/python-refactoring-exceptions-context-manager/",
    "title": "Quick Python: Refactoring Exceptions with Context Manager",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI generally find exception syntax a little clunky\u0026hellip;\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003etry\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e _ \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e range(\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        sleep(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eexcept\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eKeyboardInterrupt\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Awesome task 1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Awesome task 2...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003epass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eEspecially if you end up capturing the same exceptions and handling it the same way.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003etry\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e _ \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e range(\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        sleep(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eexcept\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eKeyboardInterrupt\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Awesome task 1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Awesome task 2...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003epass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003etry\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e _ \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e range(\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        sleep(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eexcept\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eKeyboardInterrupt\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Awesome task 1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Awesome task 2...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003epass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOne way to make our code more DRY (don\u0026rsquo;t-repeat-yourself) is to make use of Python\u0026rsquo;s context managers.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@contextmanager\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehandle_sigint\u003c/span\u003e():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003etry\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eyield\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eexcept\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eKeyboardInterrupt\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# Awesome task 1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# Awesome task 2...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003epass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUsing the context manager, everything within the indented block gets executed within the try block.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e handle_sigint():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e _ \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e range(\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        sleep(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e handle_sigint():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e _ \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e range(\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        sleep(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn fact, we can write this in a generic way to give us an alternative syntax for handling exceptions.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@contextmanager\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehandle_exception\u003c/span\u003e(f, \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eexceptions):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003etry\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eyield\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eexcept\u003c/span\u003e exceptions \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        f(e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor example, let\u0026rsquo;s tell the user that we\u0026rsquo;re explicitly ignoring their exception\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eignore\u003c/span\u003e(e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Ignoring\u0026#34;\u003c/span\u003e, e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e__class__\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e__name__)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e handle_exception(ignore, \u003cspan style=\"color:#a6e22e\"\u003eNotImplementedError\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003eKeyboardInterrupt\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e _ \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e range(\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        sleep(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2024.02.01",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/mapping-states-visited/",
    "url": "https://brandonrozek.com/blog/mapping-states-visited/",
    "title": "Mapping the states I visited",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://hacdias.com/impossible-list/\"\u003eHenrique\u0026rsquo;s Impossible List\u003c/a\u003e shows a list of countries that they\u0026rsquo;ve visited so far and their goals for the future. I personally have not travelled to many countries, but I have visited a several US states!\u003c/p\u003e\n\u003cp\u003eInstead of presenting the states in a list, I thought it would be very cool to visually show it on a map. Ideally the map would be encoded in a SVG so that:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThe image will scale regardless of the device size\u003c/li\u003e\n\u003cli\u003eI can manually edit the source to show the visited regions\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eNow imagine my surprise after searching on \u003ca href=\"https://kagi.com/\"\u003eKagi\u003c/a\u003e to come across a SVG in the public domain on \u003ca href=\"https://commons.wikimedia.org/wiki/File:Blank_US_Map_(states_only).svg\"\u003eWikipedia\u003c/a\u003e. Even better, the following is shown near the top of the file\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/* Individual states can be colored as follows: */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eks\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003emt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003epa\u003c/span\u003e {fill:\u003cspan style=\"color:#ae81ff\"\u003e#0000FF\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eca\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ede\u003c/span\u003e {fill:\u003cspan style=\"color:#ae81ff\"\u003e#FF0000\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/* In this example, Kansas, Montana and Pennsylvania are colored blue,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eand California and Delaware are colored red.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003ePlace this code in the empty space below. */\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSuch an elegant way of coloring in the states! Now it\u0026rsquo;s up to me to come up with some categories. As of the time of writing, I decided on:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eGreen: Places I\u0026rsquo;ve lived\u003c/li\u003e\n\u003cli\u003eBlue: Places I\u0026rsquo;ve visited\u003c/li\u003e\n\u003cli\u003eDarker Gray: Places I\u0026rsquo;ve driven through\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eHere\u0026rsquo;s how I colored by map:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/usa_visited.svg\" alt=\"Map of US States colored by whether I\u0026rsquo;ve visited\"\u003e\u003c/p\u003e\n\u003cp\u003eFor the last couple weeks, I\u0026rsquo;ve been brainstorming new pages for my website and came up with \u003ca href=\"https://brandonrozek.com/visited\"\u003ehttps://brandonrozek.com/visited\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eAt the time of writing, it only has the colored in states and a list of cities I\u0026rsquo;ve been to. Though I\u0026rsquo;m welcome to any suggestions you might have. Feel free to \u003ca href=\"/contact\"\u003eget in touch!\u003c/a\u003e\u003c/p\u003e\n",
    "date_published": "2024.02.01",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/wordguess/",
    "url": "https://brandonrozek.com/blog/wordguess/",
    "title": "Wordguess: A game for the Tildeverse",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe \u003ca href=\"/blog/tildeverse/\"\u003eTildeverse\u003c/a\u003e is a collection of online communities that allows people to create user accounts on public unix machines (pubnixes). Often this is to create websites, however, one component that I think deserves additional attention is the creation of games for these communities.\u003c/p\u003e\n\u003cp\u003eThree games that are featured in the message-of-the-day in Tilde.club are:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://tildegit.org/team/botany\"\u003eBotany\u003c/a\u003e: Plant raising game where players can help water each other\u0026rsquo;s plants\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://github.com/jmdejong/asciifarm\"\u003eAsciifarm\u003c/a\u003e: Multiplayer farming/fighting game\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://crawl.develz.org/\"\u003eDungeon Crawl Stone Soup\u003c/a\u003e: Rouge-like dungeon adventure\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThese games are designed to be multiplayer, but it doesn\u0026rsquo;t need to in order to be a social game. A large example of this recently, is the game Wordle.\u003c/p\u003e\n\u003cp\u003eWordle is designed to be played by oneself. A challenge is issued once a day, and the player gets a score based on their performance. The players can then compare the scores with each other and strive to beat each other on future days.\u003c/p\u003e\n\u003cp\u003eWordGuess is a Wordle inspired game I made designed to run on a pubnix. It relies on the Linux permission system to authenticate players and keeps a leaderboard showing players scores for each of the days.\u003c/p\u003e\n\u003cp\u003eBelow is how it looks like for a player to SSH into the pubnix and play the game:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/wordguess.svg\" alt=\"Gameplay of WordGuess\"\u003e\u003c/p\u003e\n\u003cp\u003eFor those on the \u003ca href=\"https://tilde.club/\"\u003eTilde.club\u003c/a\u003e server, it\u0026rsquo;s already setup and ready to play. Run the following command to start:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epython /home/brozek/WordGuess/client.py\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAfter playing the game you\u0026rsquo;re welcome to see the scores of others that day:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epython /home/brozek/WordGuess/leaderboard.py\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you\u0026rsquo;re not on Tilde.club. You\u0026rsquo;re welcome to set it up yourself on any Linux server or pubnix. The instructions are listed on the \u003ca href=\"https://github.com/brandon-rozek/wordguess\"\u003eGithub\u003c/a\u003e.\u003c/p\u003e\n",
    "date_published": "2024.01.28",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/ice-hockey/",
    "url": "https://brandonrozek.com/blog/ice-hockey/",
    "title": "Ice Hockey",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cimg src=\"/files/images/blog/20231126210415162.jpg\" alt=\"Action shot of ice hockey players\"\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eOne of my favorite pastimes of late is watching my university\u0026rsquo;s hockey team. RPI Men\u0026rsquo;s hockey compete in the \u003ca href=\"https://en.wikipedia.org/wiki/ECAC_Hockey\"\u003eECAC hockey league\u003c/a\u003e which is a division 1 league under the \u003ca href=\"https://en.wikipedia.org/wiki/National_Collegiate_Athletic_Association\"\u003eNCAA\u003c/a\u003e. In other words, hockey is a pretty big deal at the university,\u003c/p\u003e\n\u003cp\u003eHockey is a full-contact sport with players going back and forth on the ice at high speeds. You can tell it\u0026rsquo;s a strenuous sport because the players only stay on the ice for a shift of on average 45 seconds.\u003c/p\u003e\n\u003cp\u003ePart of the fun is the camaraderie among the attendees. We have a full set of \u003ca href=\"http://apbwiki.rpipepbandalumni.org/Category:Vocal_cheers\"\u003evocal cheers\u003c/a\u003e that we scream when penalties are announced, goals are obtained, and other in-game events. We call them vocal cheers, however, the majority of them consists of yelling that the other team \u003cem\u003esucks\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve attended at least 5 games at this point. I wouldn\u0026rsquo;t say that I\u0026rsquo;m an expert at  the rules of hockey, but I\u0026rsquo;ve picked up on the most common calls.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eFor resets:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eIcing: When a player shoots a puck before the center line and it passes the goal line on the other side. This resets the game with the puck going to the defending zone of the team that committed the infraction.\u003c/li\u003e\n\u003cli\u003ePuck out of bounds: When a player unintentionally shoots a puck out of the rink either hitting the audience area or the netting. This resets the game to the closest face off spot from where the puck was hit.\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003cp\u003eIt\u0026rsquo;s also not hockey without talking about penalties.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eBoarding: Violently pushing a player onto the walls of the rink\u003c/li\u003e\n\u003cli\u003eCross-checking: Hitting an opponent with a stick when both hands are on the stick and no part of the stick is on the ice.\u003c/li\u003e\n\u003cli\u003eHooking: Using the stick to \u0026ldquo;hook\u0026rdquo; or slow down an opponent.\u003c/li\u003e\n\u003cli\u003eSlashing: Swinging the stick at an opponent.\u003c/li\u003e\n\u003cli\u003eTripping: Causing an opponent to fall.\u003c/li\u003e\n\u003cli\u003eInterference: Impeding a player that does not have a puck.\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003cp\u003eMost of these penalties (unless injury occurs) result in a 2 minute timeout for the player that committed the infraction. The player does not get replaced, resulting in the team that committed the infraction having one less active player. This time period is marked as a \u003cem\u003epower play\u003c/em\u003e for the other team.\u003c/p\u003e\n\u003cp\u003eThere are 3 periods in ice hockey with each period lasting 20 minutes. If there are any special performances at RPI, it typically happens after the first period. For example, we\u0026rsquo;ve had several ice skating performances. After the second period, RPI holds the  \u0026ldquo;chuck the puck\u0026rdquo; competition and the 50/50 raffle. \u003ca href=\"https://en.wikipedia.org/wiki/Ice_resurfacer\"\u003eZambonis\u003c/a\u003e come out after these events to resurface the ice.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20230212014049350.jpg\" alt=\"Image of Zamboni sponsored by CDATA\"\u003e\u003c/p\u003e\n\u003cp\u003eLastly, we cannot forget about the pep band and our good ol\u0026rsquo; mascot Puckman. They don\u0026rsquo;t come out every  game but it\u0026rsquo;s great when they do.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20230212011745325.jpg\" alt=\"Puckman\"\u003e\u003c/p\u003e\n",
    "date_published": "2024.01.25",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/marshalling-python-dataclasses/",
    "url": "https://brandonrozek.com/blog/marshalling-python-dataclasses/",
    "title": "Marshalling Python Dataclasses",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eRecently I wanted a way to transfer structured messages between two python applications over a unix domain socket. The cleanest and simplest way I found so far is to make use of the dataclasses and json standard libraries.\u003c/p\u003e\n\u003cp\u003eWe\u0026rsquo;ll consider the following message for the rest of the post:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e dataclasses \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e dataclass\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@dataclass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eQueryUserMessage\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    auth_key: str\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    username: str\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"marshalling\"\u003eMarshalling\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s say we have a message we want to send:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emessage \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e QueryUserMessage(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;lkajdfsas\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;brozek\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe first need to get its dictionary representation. Luckily the standard library has us there:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e dataclasses \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e asdict\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emessage_dict \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e asdict(message)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen we can use the \u003ccode\u003ejson\u003c/code\u003e module to give us a string representation\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e json\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emessage_str \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e json\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003edumps(message_dict)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFinally, we can encode it into bytes and send it away:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Default encoding is \u0026#34;utf-8\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emessage_bytes \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e message_str\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eencode()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Assuming connetion is defined...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econnection\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esendall(message_bytes)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo make this easier for myself, I create a custom \u003ccode\u003ejson\u003c/code\u003e encoder and a function that uses the connection to send off the message\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDataclassEncoder\u003c/span\u003e(json\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eJSONEncoder):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edefault\u003c/span\u003e(self, o):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e asdict(o)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esend_message\u003c/span\u003e(connection, message_dataclass):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    contents \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e json\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003edumps(message_dataclass, cls\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eDataclassEncoder)\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eencode()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    connection\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esendall(contents)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"un-marshalling\"\u003eUn-marshalling\u003c/h2\u003e\n\u003cp\u003eOn the other end, let us receive the bytes and decode it into a string:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMESSAGE_BUFFER_LEN \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1024\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emessage_bytes \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e connection\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003erecv(MESSAGE_BUFFER_LEN)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emessage_str \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e message_bytes\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003edecode()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can use the \u003ccode\u003ejson\u003c/code\u003e module to turn it into a Python dictionary\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emessage_dict \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e json\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eloads(message_str)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn this post, we can make use of the fact that we only have one message class. In other cases, you would either want to rely on some protocol or pass in the message type ahead of time. Therefore, we can pass the fields of the dictionary straight to the constructor.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emessage \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e QueryUserMessage(\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003emessage_dict)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eIn production use cases, we\u0026rsquo;ll need to introduce a gambit of error-handling to capture failures in json de-serialization and class instantiation. I hope, however, that this serves as a good starting point.\u003c/p\u003e\n\u003cp\u003eSome things to consider:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eIf you have multiple types of messages, maybe including a field in the dictionary that is a string which represents the message type. Both applications can then maintain a map between these strings and the class constructors.\u003c/li\u003e\n\u003cli\u003eIf it\u0026rsquo;s possible to have messages larger than the buffer length, then consider either setting it higher or sending the size of the message beforehand.\u003c/li\u003e\n\u003cli\u003eUsing a standard HTTP library ;)\u003c/li\u003e\n\u003c/ol\u003e\n",
    "date_published": "2024.01.20",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/tildeverse-ascii-towns-cadastre/",
    "url": "https://brandonrozek.com/blog/tildeverse-ascii-towns-cadastre/",
    "title": "Ascii Towns in the Tildeverse with Cadastre",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://github.com/jmdejong/cadastre\"\u003eCadastre\u003c/a\u003e stitches together plots of \u0026ldquo;land\u0026rdquo; that users on a tilde server control. Ultimately creating one giant ascii town.\u003c/p\u003e\n\u003cp\u003eFor example,  consider one user has the following in \u003ccode\u003e~/.cadastre/home.txt\u003c/code\u003e:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e0 0\n####################\n#  ________        #\n# /~\\__\\___\\       #\n# | |  |   | ,,,,  #\n#           ,,,,,  #\n#    ~~~~~  ,,,,,  #\n#    ~~~~~     _   #\n#    ~~~~~    (*)  #\n# ~deepend     |   #\n#                  #\n####################\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWhile another user has:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e0 1\n~~~~~~~~~~~~~~~~~~~~~~~~\n~~~~~~~~~~~~~~~~~~~~~~~\n~~~~~~~~~~~~~~~~~~~~~~~\n~~~~~~~~~~~~~~~~~~~~~~~\n~~        ~~~~~~~~~~~~~\n~    ~~~      ~~~~~~~~~~\n~~~~~~~~~      ~~~    ~\n~~~~~~~~~~~         ~~~\n~~~~~~~~~~~~~~~~~~~~~~~\n~~~~~~~~~~~~~~~~~~~~~~~\n~~~~~~~~~~~~~~~~~~~~~~~\n~~~~~~~~~~~~~~~~~~~~~~~\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThen the stitched together town will be:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#################### ~~~~~~~~~~~~~~~~~~~~~~~~\n#  ________        # ~~~~~~~~~~~~~~~~~~~~~~~\n# /~\\__\\___\\       # ~~~~~~~~~~~~~~~~~~~~~~~\n# | |  |   | ,,,,  # ~~~~~~~~~~~~~~~~~~~~~~~\n#           ,,,,,  # ~~        ~~~~~~~~~~~~~\n#    ~~~~~  ,,,,,  # ~    ~~~      ~~~~~~~~~~\n#    ~~~~~     _   # ~~~~~~~~~      ~~~    ~\n#    ~~~~~    (*)  # ~~~~~~~~~~~         ~~~\n# ~deepend     |   # ~~~~~~~~~~~~~~~~~~~~~~~\n#                  # ~~~~~~~~~~~~~~~~~~~~~~~\n#################### ~~~~~~~~~~~~~~~~~~~~~~~\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAscii art is something that always interested me, but I never took the time to make. Cadastre limits us to 24x12 characters which serves as a good starting canvas for myself.\u003c/p\u003e\n\u003cp\u003eYou might be able to tell, but I\u0026rsquo;m a big fan of mountains.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e+=======================+\n.    /\\                 .\n.   /  \\  /\\            .\n.  /    \\/  \\/\\         .\n. /      \\ _ \\ \\        .\n.     ____//            .\n. ___/____/      @@@    .\n. ___/         @@@@@@@  .\n.             @@@@@@@@@ .\n.              @@@@@@@  .\n. ~brozek        | |    .\n.........................\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eCheck out my plot among the village over at \u003ca href=\"https://tilde.club/~troido/cadastre/town.html\"\u003etilde.club\u003c/a\u003e. For a more lively instance check out \u003ca href=\"http://tilde.town/~troido/cadastre/town.html\"\u003etilde.town\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eThanks to \u003ca href=\"http://tilde.town/~troido/\"\u003e~troido\u003c/a\u003e for creating this cool piece of software!\u003c/em\u003e\u003c/p\u003e\n",
    "date_published": "2024.01.17",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/python-dataclasses-derived-fields-validation/",
    "url": "https://brandonrozek.com/blog/python-dataclasses-derived-fields-validation/",
    "title": "Python Dataclasses: Derived Fields and Validation",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003ePython dataclasses provide a simplified way of creating simple classes that hold data.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e dataclasses \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e dataclass\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@dataclass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ePerson\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    name: str\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    birth_year: int\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe above code is equivalent to:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eA\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __init__(name: str, birth_year: int):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e name\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebirth_year \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e birth_year\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e__post__init__()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNotice the call to \u003ccode\u003e__post__init__\u003c/code\u003e at the end. We can override that method to do whatever we\u0026rsquo;d like. I have found two great use cases for this.\u003c/p\u003e\n\u003ch2 id=\"use-case-1-derived-fields\"\u003eUse Case 1: Derived Fields\u003c/h2\u003e\n\u003cp\u003eStraight from the \u003ca href=\"https://docs.python.org/3/library/dataclasses.html#dataclasses.__post_init__\"\u003ePython documentation\u003c/a\u003e, this use case is for when we want to use some variables to create a new variable.\u003c/p\u003e\n\u003cp\u003eFor example, to compute a new field \u003ccode\u003eage\u003c/code\u003e from a person\u0026rsquo;s \u003ccode\u003ebirth_year\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ePerson\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    name: str\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    birth_year: int\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    age: int \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e field(init\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eFalse\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003e__post_init__\u003c/span\u003e(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# Assuming the current year is 2024 and their birthday already passed\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eage \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2024\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebirth_year\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"use-case-2-validation\"\u003eUse Case 2: Validation\u003c/h2\u003e\n\u003cp\u003eAnother use case is to make sure\nthat the user instantiates the fields\nof a data class in a way we expect.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ePerson\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    name: str\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    birth_year: int\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003e__post__init__\u003c/span\u003e(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eassert\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebirth_year \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eassert\u003c/span\u003e isinstance(self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename, str)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNothing is stopping us from combining both of these use cases within the \u003ccode\u003e__post_init__\u003c/code\u003e method!\u003c/p\u003e\n",
    "date_published": "2024.01.15",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/fun-with-telnet/",
    "url": "https://brandonrozek.com/blog/fun-with-telnet/",
    "title": "Fun with Telnet",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eSome time ago I came across a cinematic experience\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e.........                @@@@@    @@@@@      ...........           \n.........               @     @  @     @     ...........           \n .........                 @@@   @     @      ..........           \n  ........               @@      @     @       .........           \n   ........             @@@@@@@   @@@@@  th     .......            \n     ......           -----------------------    ......            \n       .....            C  E  N  T  U  R  Y        ....            \n         ....         -----------------------       ...            \n           ...        @@@@@ @@@@@ @   @ @@@@@        ..            \n            ==          @   @      @ @    @          ==            \n          __||__        @   @@@@    @     @        __||__          \n         |      |       @   @      @ @    @       |      |         \n_________|______|_____  @   @@@@@ @   @   @  _____|______|_________\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIt was a Star Wars inspired animation, brought to you by Telnet!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etelnet towel.blinkenlights.nl \u003cspan style=\"color:#ae81ff\"\u003e23\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cem\u003eIt\u0026rsquo;s worth watching the entire thing.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eFast forward a few years, and I\u0026rsquo;ve come across other great \u003ca href=\"https://www.telnet.org/htm/places.htm\"\u003etelnet nuggets\u003c/a\u003e\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eAddress\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003efreechess.org 5000\u003c/td\u003e\n          \u003ctd\u003eChess \u003ca href=\"https://www.freechess.org/\"\u003e[1]\u003c/a\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003etowel.blinkenlights.nl 23\u003c/td\u003e\n          \u003ctd\u003eStar Wars asciimation\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003emtrek.com 1701\u003c/td\u003e\n          \u003ctd\u003eSpace combat game (inspired by StarTrek) \u003ca href=\"http://mtrek.com/\"\u003e[1]\u003c/a\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003efibs.com 4321\u003c/td\u003e\n          \u003ctd\u003eBackgammon \u003ca href=\"http://fibs.com/\"\u003e[1]\u003c/a\u003e \u003ca href=\"https://tinysubversions.com/notes/backgammon-fibs/\"\u003e[2]\u003c/a\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003emapscii.me\u003c/td\u003e\n          \u003ctd\u003eWorld map (Can zoom in with mouse) \u003ca href=\"https://github.com/rastapasta/mapscii\"\u003e[1]\u003c/a\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003etelehack.com\u003c/td\u003e\n          \u003ctd\u003eArpanet/Usenet Simulation. Includes over 60 text-based games \u003ca href=\"https://telehack.com/\"\u003e[1]\u003c/a\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eRemember that telnet communication is all over plaintext, so don\u0026rsquo;t accidentally leak any secrets. Have fun discovering the quirky old world of Telnet!\u003c/p\u003e\n",
    "date_published": "2024.01.08",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/rss-feed-watchlist-wikipedia/",
    "url": "https://brandonrozek.com/blog/rss-feed-watchlist-wikipedia/",
    "title": "How to get a RSS Feed of your Wikipedia Watchlist",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eInstead of having to manually log in to Wikipedia to look at your watchlist, you can use your RSS feed!\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eStep 1:\u003c/strong\u003e Get your token\u003c/p\u003e\n\u003cp\u003eGo to your \u003ca href=\"https://en.wikipedia.org/wiki/Special:Preferences#mw-prefsection-watchlist-token\"\u003epreferences\u003c/a\u003e. Log in if not already.\u003c/p\u003e\n\u003cp\u003eThen click on the \u003cem\u003eWatchlist\u003c/em\u003e tab. Scroll down to \u003cem\u003eTokens\u003c/em\u003e. Click the \u003cem\u003eManage Tokens\u003c/em\u003e button.\u003c/p\u003e\n\u003cp\u003eHold on to the token for the next step.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eStep 2:\u003c/strong\u003e Import the RSS feed to your feed reader.\u003c/p\u003e\n\u003cp\u003eDirect URL: \u003ca href=\"https://en.wikipedia.org/w/api.php?action=feedwatchlist\u0026amp;allrev\u0026amp;wlowner=USERNAME\u0026amp;wltoken=TOKEN\"\u003ehttps://en.wikipedia.org/w/api.php?action=feedwatchlist\u0026amp;allrev\u0026amp;wlowner=USERNAME\u0026amp;wltoken=TOKEN\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eReplace \u003ccode\u003eUSERNAME\u003c/code\u003e and \u003ccode\u003eTOKEN\u003c/code\u003e with their respective information.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThat\u0026rsquo;s it!\u003c/strong\u003e Each new item in the RSS feed will look like the following (feed reader dependent):\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eReinforcement learning\nDecember 31, 2023 at 11:28 PM by OAbot\n\n[[Wikipedia:OABOT|Open access bot]]: arxiv updated in citation with #oabot. (OAbot) \n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAt a glance, you can see:\u003c/p\u003e\n\u003cp\u003eLine 1: Title of Wikipedia Page Edited\u003c/p\u003e\n\u003cp\u003eLine 2: Time and Author of Edit (may be username or IP)\u003c/p\u003e\n\u003cp\u003eRest: Description of edit.\u003c/p\u003e\n\u003cp\u003eFrom here you can check the diffs to make sure that the edit stands.\u003c/p\u003e\n\u003ch2 id=\"resources\"\u003eResources\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://m.mediawiki.org/wiki/Help:Watchlist\"\u003ehttps://m.mediawiki.org/wiki/Help:Watchlist\u003c/a\u003e\n\u003ca href=\"https://en.m.wikipedia.org/wiki/Wikipedia%3aSyndication\"\u003ehttps://en.m.wikipedia.org/wiki/Wikipedia%3aSyndication\u003c/a\u003e\u003c/p\u003e\n",
    "date_published": "2024.01.07",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/command-when-file-in-directory-changes/",
    "url": "https://brandonrozek.com/blog/command-when-file-in-directory-changes/",
    "title": "Run a command when a file in a directory changes",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen editing code or adding content to a HTML file for a website, it can be very useful to see the changes right away after you save the file. We\u0026rsquo;ll show a lightweight way of accomplishing this task with \u003ccode\u003eentr\u003c/code\u003e and a bit of bash scripting.\u003c/p\u003e\n\u003cp\u003eFirst if you don\u0026rsquo;t already have it, install \u003ccode\u003eentr\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dnf install entr\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis terminal application works by monitoring files passed within standard in. Therefore, to run a \u003ccode\u003e$command\u003c/code\u003e  if any file under some \u003ccode\u003e$directory\u003c/code\u003e changes run the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efind $directory -type f | entr $command\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAn example can be running a build script if anything under the source folder changes:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efind src -type f | entr ./build.sh\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow this doesn\u0026rsquo;t capture files that get added to the directory. To do this, we need to get \u003ccode\u003eentr\u003c/code\u003e to monitor the parent folder for any changes. We can do this with the \u003ccode\u003e-d\u003c/code\u003e flag.\u003c/p\u003e\n\u003cp\u003eFrom the man page:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e-d\u003c/strong\u003e    Track the directories of regular files provided as input and exit if\na new file is added.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eSince \u003ccode\u003eentr\u003c/code\u003e exits when a file gets added with this flag, the \u003ca href=\"https://superuser.com/a/665208\"\u003ecommon solution\u003c/a\u003e on the internet is to wrap it in a \u003ccode\u003ewhile\u003c/code\u003e loop.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$ \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e sleep 0.1 ; \u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e find $directory -type f | entr -d $command ; \u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis solution works great as a one-liner, but it doesn\u0026rsquo;t let me CTRL-C out when I\u0026rsquo;m finished. Therefore, I wrote a shell script that incorporates this solution while also adding a \u003ccode\u003etrap\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o nounset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o pipefail\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eshow_usage\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Usage: entr-dir [dir] [command]\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check argument count\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$#\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -lt \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    show_usage\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Make sure that the command entr exists\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e ! command -v entr \u0026gt; /dev/null ; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;entr not found. Exiting...\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDIR\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e; shift\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[[\u003c/span\u003e ! -d \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$DIR\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;First argument must be a directory. Exiting...\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Allow for CTRL-C to exit script\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etrap \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;exit 0;\u0026#34;\u003c/span\u003e SIGINT\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e sleep 0.1; \u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    find \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$DIR\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -type f | entr -d \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$@\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI plopped this in \u003ccode\u003e~/.local/bin/entr-dir\u003c/code\u003e, gave it the \u003ccode\u003e+x\u003c/code\u003e permission, and now I can easily monitor and build projects when files get changed/added/deleted.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eentr-dir src ./build.sh\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2024.01.05",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/bash-traps-exit-error-sigint/",
    "url": "https://brandonrozek.com/blog/bash-traps-exit-error-sigint/",
    "title": "Bash Traps: Exit, Error, Sigint",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eTraps in bash listen for interrupts and signals and performs a bash command in response to it. This post will go over three use cases I\u0026rsquo;ve encountered so far in my bash scripting journey.\u003c/p\u003e\n\u003ch2 id=\"sigint\"\u003e\u003ccode\u003eSIGINT\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eIf we have a while loop within a bash script\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e sleep 0.1; \u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  do_something\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe probably want to be able to CTRL-C to exit the whole script.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efinish \u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  exit \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etrap finish SIGINT\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e sleep 0.1; \u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  do_something\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"err\"\u003e\u003ccode\u003eERR\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eIf we encounter an error in a bash script, one thing we might want to do is dump the environment information and other things that can be useful for debugging.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o errexit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o nounset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o pipefail\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint_debug \u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  declare exit_code\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$?\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  env \u0026gt;\u0026amp;\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  other_debug_info \u0026gt;\u0026amp;\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  exit \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$exit_code\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etrap print_debug ERR\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecommand_that_may_fail\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"exit\"\u003e\u003ccode\u003eEXIT\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eLike the last one, this function will occur if the script fails. However, this also occurs if the script is successful. It\u0026rsquo;s similar to the \u003ccode\u003efinally\u003c/code\u003e clause within a \u003ccode\u003etry-catch\u003c/code\u003e programming paradigm.\u003c/p\u003e\n\u003cp\u003eThis is useful for cleaning up build files, de-provisioning services, etc.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecleanup \u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  rm a.tmp b.tmp\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  deprovision\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etrap cleanup EXIT\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elong_compile_may_fail\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"other-resources\"\u003eOther Resources\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"http://redsymbol.net/articles/bash-exit-traps/\"\u003ehttp://redsymbol.net/articles/bash-exit-traps/\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://jakski.github.io/pages/error-handling-in-bash.html\"\u003ehttps://jakski.github.io/pages/error-handling-in-bash.html\u003c/a\u003e\u003c/p\u003e\n",
    "date_published": "2024.01.02",
    "tags": ["Bash"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/no-response-default-nginx/",
    "url": "https://brandonrozek.com/blog/no-response-default-nginx/",
    "title": "No Response Default in Nginx",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cblockquote\u003e\n\u003cp\u003eWelcome to nginx!\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eAfter installing \u003ccode\u003enginx\u003c/code\u003e fresh on a server, this classic quote is part of a welcome page that is automatically setup within a default configuration file. On Redhat-based systems this lives under \u003ccode\u003e/etc/nginx/conf.d\u003c/code\u003e and for Ubuntu-based ones under \u003ccode\u003e/etc/nginx/sites-available/\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThis page mostly exists to make sure that everything is squared away after installation. However, how can we get rid of the page once we have all the site configurations loaded?\u003c/p\u003e\n\u003cp\u003eWe\u0026rsquo;ll show how to get rid of the default welcome page by changing the \u003ccode\u003edefault.conf\u003c/code\u003e file and go an extra step to ignore domain names that we\u0026rsquo;re not serving.\u003c/p\u003e\n\u003cp\u003eWhen you type in \u003ccode\u003ehttps://brandonrozek.com\u003c/code\u003e in your web browser, your system will first (1) look up the IP (at the time of writing it\u0026rsquo;s \u003ccode\u003e173.255.230.230\u003c/code\u003e) and then (2) send a HTTP request with the header including \u003ccode\u003eHost('brandonrozek.com')\u003c/code\u003e. It\u0026rsquo;s the \u003ccode\u003eHost\u003c/code\u003e header that lets \u003ccode\u003enginx\u003c/code\u003e know which content to serve.\u003c/p\u003e\n\u003cp\u003eSo if \u003ccode\u003enginx\u003c/code\u003e comes across a \u003ccode\u003eHost\u003c/code\u003e header that doesn\u0026rsquo;t have a specified configuration for, then it follows some default behavior. This is described under the configuration file with the host \u003ccode\u003edefault_server\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eOne good default behavior is to not bother responding to the request. For this purpose, the best HTTP response code is \u003ccode\u003e444\u003c/code\u003e No Response.\u003c/p\u003e\n\u003cp\u003eThe \u003ccode\u003enginx\u003c/code\u003e configuration on the HTTP side looks like the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eserver\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003edefault_server\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e[::]:80\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eserver_name\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e_\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e444\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUnder standard HTTPS, a SSL handshake occurs. Though this seems pointless if all we\u0026rsquo;re doing is returning nothing. Therefore, we can reject the handshake as well.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eserver\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e443\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003essl\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e[::]:443\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003essl\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ehttp2\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eon\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003essl_reject_handshake\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eon\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eserver_name\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e_\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e444\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow we\u0026rsquo;ll want to save this to a file called \u003ccode\u003e_default.conf\u003c/code\u003e. Note that it has an underscore in the front. This is because we want this file to be the first configuration file to appear in our folder. When a client connects via HTTPS (unless we have SNI enabled), we have no way to know which host name they wish to connect to. That\u0026rsquo;s because that information is encrypted! The default behavior in nginx is to then use the first configuration file.\u003c/p\u003e\n\u003cp\u003eAll together, \u003ccode\u003e_default.conf\u003c/code\u003e looks like:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eserver\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003edefault_server\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e[::]:80\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eserver_name\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e_\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e444\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eserver\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e443\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003essl\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e[::]:443\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003essl\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ehttp2\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eon\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003essl_reject_handshake\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eon\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eserver_name\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e_\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e444\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can test to see if this works by trying to query your IP directly using curl.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl 173.255.230.230\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis should return:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ecurl: (52) Empty reply from server\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2024.01.01",
    "tags": ["Nginx"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/switching-raid-10/",
    "url": "https://brandonrozek.com/blog/switching-raid-10/",
    "title": "Switching to Raid 10",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn my living room lies a Dell PowerEdge R430 server which acts as the primary data storage system in our home. The server has four hard drive slots, three of which I had previously filled.\u003c/p\u003e\n\u003cp\u003eI run \u003ccode\u003ebtrfs\u003c/code\u003e as my primary file system on that server. The file system supports multiple raid levels: RAID 0, RAID 1, RAID 10, RAID 5, and RAID 6. Wikipedia has two great articles describing the \u003ca href=\"https://en.wikipedia.org/wiki/Standard_RAID_levels\"\u003estandard RAID levels\u003c/a\u003e and \u003ca href=\"https://en.wikipedia.org/wiki/Nested_RAID_levels\"\u003enested RAID levels\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eSince at the time I only had 3 drives, it limited me to RAID 0, 1, and 5. There are still \u003ca href=\"https://btrfs.readthedocs.io/en/latest/btrfs-man5.html#raid56-status-and-recommended-practices\"\u003esome complications with RAID 5\u003c/a\u003e in btrfs, so it leaves me with two options:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eRAID 0: No data redundancy. Data is split evenly across all disks\u003c/li\u003e\n\u003cli\u003eRAID 1: Mirroring. Data is present across all disks.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eNow the data isn\u0026rsquo;t vital to me. I have backups \u003ca href=\"https://www.raidisnotabackup.com/\"\u003eelsewhere\u003c/a\u003e. However, I would prefer not to deal with the process of restoring terrabytes of data from a backup when a hard drive fails. So at the time, I used RAID 1.\u003c/p\u003e\n\u003cp\u003eFor Christmas, I received another 3.5 inch hard drive for my server. This now unlocks the two other RAID levels 6 and 10. RAID 6 suffers from similar issues as RAID 5. What about RAID 10?\u003c/p\u003e\n\u003cp\u003eRAID 10 is a nested RAID level because it has both a RAID 1 and RAID 0 setup with it.  Wikipedia has a \u003ca href=\"https://en.wikipedia.org/wiki/Nested_RAID_levels#/media/File:RAID_10_01.svg\"\u003egreat diagram\u003c/a\u003e which I\u0026rsquo;ll show here describing how it\u0026rsquo;s laid out.\u003c/p\u003e\n\n\u003cimg style=\"display: block; margin: 0 auto;\" src=\"/files/images/blog/202312291631.png\" alt=\"Diagram describing RAID 10\" width=250/\u003e\n\n\u003cp\u003eFor four hard drives, we split them into pairs of two. Each pair is configured as RAID 1. Meaning that for a pair (A, B), all the data in A is also in B. Then all of the pairs  (A, B), (C, D) get combined into a RAID 0 configuration. This means that data from one pair (A, B) will not appear in another pair (C, D).\u003c/p\u003e\n\u003cp\u003eRAID 10, just like RAID 1 protects against a single drive failure. For RAID 1, this is because the data is stored on all the other hard drives. For RAID 10, this is because the data is on the hard drives corresponding pair.\u003c/p\u003e\n\u003cp\u003eRAID 10, however, may or may not be safe if two hard drives fail. If you\u0026rsquo;re lucky, only a single hard drive from each of the pairs fail. However, if both A and B fail and they\u0026rsquo;re in a (A, B) pair, the data is lost.\u003c/p\u003e\n\u003cp\u003eThe benefit? You get to utilize more of the storage space compared to a RAID 1 configuration. In RAID 1, you can only use up to the smallest disk size you have. This is because the data needs to exist on all the hard drives. For RAID 10, it\u0026rsquo;s dependent on how the drives are paired up. If all the hard drives are the same size, however, then you can use double the space than a corresponding RAID 1 configuration.\u003c/p\u003e\n\u003cp\u003eHard drives don\u0026rsquo;t fail every day, in fact it\u0026rsquo;s common for hard drives to \u003ca href=\"https://www.backblaze.com/blog/hard-drive-life-expectancy/\"\u003elast beyond 3-5 years\u003c/a\u003e. Therefore it makes sense to me to sacrifice the ability for all-but-one hard drives to fail for more storage capacity. Especially since I regularly obtain hard drives many months apart, to avoid the \u003ca href=\"https://en.wikipedia.org/wiki/Bathtub_curve\"\u003ebathtub curve\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThe steps for adding the hard drive and converting the raid level weren\u0026rsquo;t too involved.\u003c/p\u003e\n\u003cp\u003eTo add a drive:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo btrfs device add /dev/sdX MOUNTPOINT\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReplacing \u003ccode\u003esdX\u003c/code\u003e with the appropriate hard drive reference.\u003c/p\u003e\n\u003cp\u003eTo convert to RAID 10:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo btrfs balance start -dconvert\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eraid10 -mconvert\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eraid10 MOUNTPOINT\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDepending on the amount of data you have, it can take many hours (sometimes days) to balance a btrfs file system. Therefore, it might make sense to run the above command in a \u003ccode\u003etmux\u003c/code\u003e session.\u003c/p\u003e\n",
    "date_published": "2023.12.29",
    "tags": ["Storage"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/renewing-gpg-keys/",
    "url": "https://brandonrozek.com/blog/renewing-gpg-keys/",
    "title": "Renewing my GPG Keys",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eRecently I let my GPG keys expire. I noticed this when I was working on a project, and when I went to \u003ca href=\"/blog/signingcommits/\"\u003eautomatically sign my commits\u003c/a\u003e \u0026ndash; git threw an error at me. Since I was working at the time, I did the not-so-great practice of disabling the signing feature.\u003c/p\u003e\n\u003cp\u003eHaving keys automatically expire is annoying. Though, it does give me a chance to reflect if these keys are still useful to me.  Currently I use GPG keys for:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eCode signing\u003c/li\u003e\n\u003cli\u003eReceiving encrypted messages\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/blog/decentralized-identity-pgp-keyoxide/\"\u003eDecentralized Identity\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eSo to me, having a GPG key is still worth it. Now to go about renewing my keys. This post will show how I go about the renewing process itself and what services I update. Mostly for me in the future.\u003c/p\u003e\n\u003ch2 id=\"renewing-my-gpg-key\"\u003eRenewing my GPG key\u003c/h2\u003e\n\u003cp\u003eFirst, find your key\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egpg --list-keys\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/home/brandon/.gnupg/pubring.kbx\n------------------------------\npub   ed25519 2022-12-14 [SC] [expires: 2023-12-14]\n      5F37830BFA46FF7881F47AC78DF79C3DC5FC658A\nuid           [ultimate] Brandon Rozek \u0026lt;brozek@brandonrozek.com\u0026gt;\nuid           [ultimate] Brandon Rozek \u0026lt;hello@brandonrozek.com\u0026gt;\nsub   cv25519 2022-12-14 [E] [expires: 2023-12-14]\nsub   dsa2048 2022-12-17 [S] [expires: 2023-12-14]\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe fingerprint is the line below \u003ccode\u003epub\u003c/code\u003e and for me starts with \u003ccode\u003e5F37\u003c/code\u003e. Let\u0026rsquo;s store that in a variable for easy reference later.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport FPR\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e5F37830BFA46FF7881F47AC78DF79C3DC5FC658A\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf we want to extend the expiration date to a year from today, we can use the following command:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egpg --quick-set-expire $FPR 1y\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAlternatively, you can specify an exact date with the ISO format \u003ccode\u003eYYYY-MM-DD\u003c/code\u003e or keep it relative with respect to days \u003ccode\u003ed\u003c/code\u003e, weeks \u003ccode\u003ew\u003c/code\u003e, and months \u003ccode\u003em\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eWhen we check the key again, we should see an updated expiration date\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egpg --list-keys\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/home/brandon/.gnupg/pubring.kbx\n------------------------------\npub   ed25519 2022-12-14 [SC] [expires: 2024-12-28]\n      5F37830BFA46FF7881F47AC78DF79C3DC5FC658A\nuid           [ultimate] Brandon Rozek \u0026lt;brozek@brandonrozek.com\u0026gt;\nuid           [ultimate] Brandon Rozek \u0026lt;hello@brandonrozek.com\u0026gt;\nsub   cv25519 2022-12-14 [E] [expires: 2023-12-14]\nsub   dsa2048 2022-12-17 [S] [expires: 2023-12-14]\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNotice that the two subkeys still have the old expiration date. We\u0026rsquo;ll need to update that as well. We\u0026rsquo;ll need to get their fingergrints with the following command\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egpg --list-keys --verbose --with-subkey-fingerprints\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egpg: enabled compatibility flags:\ngpg: using pgp trust model\n/home/rozek/.gnupg/pubring.kbx\n------------------------------\npub   ed25519 2022-12-14 [SC] [expires: 2024-12-27]\n      5F37830BFA46FF7881F47AC78DF79C3DC5FC658A\nuid           [ultimate] Brandon Rozek \u0026lt;brozek@brandonrozek.com\u0026gt;\nuid           [ultimate] Brandon Rozek \u0026lt;hello@brandonrozek.com\u0026gt;\nsub   cv25519 2022-12-14 [E] [expires: 2023-12-14]\n      D502A12A65F9997DAE4609C97DAEAD7BFFA8F9D3\nsub   dsa2048 2022-12-17 [S] [expires: 2023-12-14]\n      89859D1EDF70D6DC2F6BFFF226E457DA82C9F480\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eStore the fingerprints again for easy reference:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport SFPR1\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eD502A12A65F9997DAE4609C97DAEAD7BFFA8F9D3\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport SFPR2\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e89859D1EDF70D6DC2F6BFFF226E457DA82C9F480\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eExtend the expiration of the subkeys:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egpg --quick-set-expire $FPR 1y $SFPR1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egpg --quick-set-expire $FPR 1y $SFPR2\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"updating-services\"\u003eUpdating Services\u003c/h2\u003e\n\u003cp\u003eI currently allow for two ways to query my keys: OpenGPG keyserver and WKD. To update my keys on my own WKD keyserver, I followed the steps in my \u003ca href=\"/blog/decentralized-pgp-keys-wkd\"\u003etutorial on WKD\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eFor OpenGPG, I followed the instructions on their \u003ca href=\"https://keys.openpgp.org/about/usage\"\u003eusage page\u003c/a\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egpg --export your_address@example.net | curl -T - https://keys.openpgp.org \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"references\"\u003eReferences\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://www.gnupg.org/documentation/manuals/gnupg24/gpg.1.html\"\u003ehttps://www.gnupg.org/documentation/manuals/gnupg24/gpg.1.html\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://whynothugo.nl/journal/2023/07/13/extending-an-expired-gpg-key/\"\u003ehttps://whynothugo.nl/journal/2023/07/13/extending-an-expired-gpg-key/\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://brandonrozek.com/blog/decentralized-pgp-keys-wkd/\"\u003ehttps://brandonrozek.com/blog/decentralized-pgp-keys-wkd/\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://keys.openpgp.org/about/usage\"\u003ehttps://keys.openpgp.org/about/usage\u003c/a\u003e\u003c/p\u003e\n",
    "date_published": "2023.12.28",
    "tags": ["GPG/PGP"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/starbound-server-lxc/",
    "url": "https://brandonrozek.com/blog/starbound-server-lxc/",
    "title": "Starbound Server using LXC",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI recently had a gaming session with old friends and we played the game Starbound. In hopes of continuing our session in the near future, I set up a Starbound server for us all to connect to.\u003c/p\u003e\n\u003cp\u003eHowever, since I\u0026rsquo;ve recently learned how to setup LXC containers, my host OS is Fedora, and the \u003ca href=\"https://developer.valvesoftware.com/wiki/SteamCMD#Ubuntu\"\u003esteamcmd\u003c/a\u003e provides instructions for Debian, Ubuntu, and Arch.  I\u0026rsquo;ve decided to set up a system container using LXC.\u003c/p\u003e\n\u003cp\u003eAlso who knows how secure these game servers are. It\u0026rsquo;s likely a good idea to virtualize them when possible\u0026hellip;\u003c/p\u003e\n\u003cp\u003eThis guide assumes you have a Ubuntu Jammy (22.04) amd64 system container configured and that you\u0026rsquo;re within the root shell of that container. For help with that, check out \u003ca href=\"/blog/lxc-fedora-38/\"\u003emy post\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"installing-starbound\"\u003eInstalling Starbound\u003c/h2\u003e\n\u003cp\u003eSince we start off with a root account, let\u0026rsquo;s create another user which we\u0026rsquo;ll call \u003ccode\u003esteam\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eadduser steam\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe\u0026rsquo;ll give \u003ccode\u003esudo\u003c/code\u003e privileges to our \u003ccode\u003esteam\u003c/code\u003e user. You can always remove this privilege after setting it up.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eusermod -aG sudo steam\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow let\u0026rsquo;s go into our steam user\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esu steam\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBefore using \u003ccode\u003eapt\u003c/code\u003e, it\u0026rsquo;s always a good idea to make sure our system is up to date.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt update \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e sudo apt upgrade\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSteam requires 32-bit libraries to work properly, so we\u0026rsquo;ll need to setup the multiverse repository and add i386 support.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install software-properties-common\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo add-apt-repository multiverse\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dpkg --add-architecture i386\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFinally, we can update our local cache and install \u003ccode\u003esteamcmd\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt update\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install steamcmd\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow let\u0026rsquo;s open our steam shell.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esteamcmd\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe should now see a \u003ccode\u003esteam\u0026gt;\u003c/code\u003e at the beginning of our prompt. We\u0026rsquo;ll tell steam to install Starbound under the Starbound folder of our \u003ccode\u003e$HOME\u003c/code\u003e directory.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eforce_install_dir /home/steam/starbound\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI\u0026rsquo;m unsure if we need to login or do this anonymously. When I set this up, I\u0026rsquo;ve logged into my steam account. Write to me if you were able to get this to work anonymously.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elogin \u0026lt;username\u0026gt; \u0026lt;password\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAfter logging in (it might ask for a 2FA code), we can install Starbound using the following command.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapp_update \u003cspan style=\"color:#ae81ff\"\u003e211820\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can then leave the steam shell.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003equit\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAt this point, we should check to see if the server binary runs as expected.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e/home/steam/starbound/linux/starbound_server\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf this works, great! I didn\u0026rsquo;t face any issues at this step, but feel free to write in if you did.\u003c/p\u003e\n\u003ch2 id=\"configuring-networking\"\u003eConfiguring Networking\u003c/h2\u003e\n\u003cp\u003eWe now have a Starbound server running from within our LXC container. Since we want players outside of the host system to access the Starbound server, we\u0026rsquo;ll need to setup some firewall rules and TCP forwarding.\u003c/p\u003e\n\u003cp\u003eFirst we\u0026rsquo;ll need to allow traffic to go into our container network.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo ufw allow in on lxdbr0\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo ufw route allow in on lxdbr0\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOn the host, we need to open up the Starbound port\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo ufw allow \u003cspan style=\"color:#ae81ff\"\u003e21025\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen we need to route TCP traffic back and forth. For this I used \u003ccode\u003enginx\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dnf install nginx\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe need to get the container IP.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elxc-ls --fancy\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAdd the following to the end of \u003ccode\u003e/etc/nginx/nginx.conf\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003estream {\n    upstream starbound {\n        server CONTAINERIP:21025;\n    }\n\n    server {\n        listen 21025;\n        proxy_pass starbound;\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eStart and enable the nginx service\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl enable --now nginx\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you face an error here, it might be because of \u003ccode\u003eselinux\u003c/code\u003e. Either disable that or configure it to allow for binding of \u003ccode\u003e21025\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eAt this point, you can try to connect to the Starbound server using the game! I didn\u0026rsquo;t face any unexpected issues at this part, but feel free to write in if you did.\u003c/p\u003e\n\u003ch2 id=\"locking-down-starbound-optional\"\u003eLocking down Starbound (Optional)\u003c/h2\u003e\n\u003cp\u003eThis part is optional, but if you don\u0026rsquo;t want any arbitrary person to connect to your Starbound server, you\u0026rsquo;ll need to lock it down.\u003c/p\u003e\n\u003cp\u003eInside your container exit the \u003ccode\u003e/home/steam/starbound/storage/starbound_server.config\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eDisallow anonymous connections\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;allowAnonymousConnections\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAdd users with usernames and passwords\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;serverUsers\u0026#34;\u003c/span\u003e : \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;user1\u0026#34;\u003c/span\u003e : \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;admin\u0026#34;\u003c/span\u003e : false,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;password\u0026#34;\u003c/span\u003e : \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;somethinghere\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;user2\u0026#34;\u003c/span\u003e : \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;admin\u0026#34;\u003c/span\u003e : false,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;password\u0026#34;\u003c/span\u003e : \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;somethinghere2\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eKeep in mind that we didn\u0026rsquo;t set up TLS so I wouldn\u0026rsquo;t put a password here that gets used anywhere else.\u003c/p\u003e\n\u003ch2 id=\"automatically-starting-up-the-starbound-server\"\u003eAutomatically Starting up the Starbound server\u003c/h2\u003e\n\u003cp\u003eFirst make sure \u003ca href=\"/blog/lxc-fedora-38/\"\u003ethe container autostarts on boot\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThen within the container, we can set up a systemd service to start the starbound server.\u003c/p\u003e\n\u003cp\u003eEdit \u003ccode\u003e/etc/systemd/system/starbound.service\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eStarboundServer\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAfter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enetwork.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWorkingDirectory\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/home/steam/starbound/linux\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eUser\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003esteam\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eGroup\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003esteam\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003esimple\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/home/steam/starbound/linux/starbound_server\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRestartSec\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e15\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRestart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ealways\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eKillSignal\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eSIGINT\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen start and enable the service\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl enable --now starbound\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2023.12.03",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/lxc-fedora-38/",
    "url": "https://brandonrozek.com/blog/lxc-fedora-38/",
    "title": "Setting up unprivileged containers with LXC on Fedora 38",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLXC is a containerization technology that allows us to create \u003cem\u003esystem containers\u003c/em\u003e. We can set it up so that we can SSH into a container and perform many of the same tasks we would on a regular Linux box. I currently have two uses cases for this.\u003c/p\u003e\n\u003cp\u003eFirst, these system containers allows me to follow instruction documentation for projects that do not treat docker/podman as a first class distribution method. Maybe the project relies on being able to access \u003ccode\u003esystemd\u003c/code\u003e, or perhaps I don\u0026rsquo;t want the additional burden of ensuring the upgrade path of a custom docker-based method.\u003c/p\u003e\n\u003cp\u003eSecond, it allows me to provide a lightweight virtualized environment for my friends and family. With this approach, we don\u0026rsquo;t have to set caps on the amount of CPU and memory usage from the start (as opposed to a VM), and instead impose them later if others are violating fair use.\u003c/p\u003e\n\u003cp\u003eIn this guide, we\u0026rsquo;ll focus on setting up unprivileged containers. These are containers that are started up and maintained by a user other than \u003ccode\u003eroot\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThis technology is moving fast. It\u0026rsquo;s likely that this guide will be out of date by the time you\u0026rsquo;re reading this. I do hope, however, that this can serve a as a good jumping off point for other queries.\u003c/p\u003e\n\u003ch2 id=\"setup\"\u003eSetup\u003c/h2\u003e\n\u003cp\u003eFirst, let\u0026rsquo;s install \u003ccode\u003elxc\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dnf install lxc lxc-templates\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo setup networking for our containers, we\u0026rsquo;ll also need to install \u003ccode\u003ednsmasq\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dnf install dnsmasq\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe now need to tell LXC that our user is allowed to create a certain number of network devices on our \u003ccode\u003elxcbr0\u003c/code\u003e bridge that LXC configures for us.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eid -un\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e veth lxcbr0 \u003cspan style=\"color:#ae81ff\"\u003e50\u003c/span\u003e | sudo tee -a /etc/lxc/lxc-usernet\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI can\u0026rsquo;t imagine myself creating more than 50 system containers, but do adjust that number as you see fit.\u003c/p\u003e\n\u003cp\u003eThe last command we\u0026rsquo;ll need to run as root on the host system is to enable and start the \u003ccode\u003elxc-net\u003c/code\u003e service.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl enable --now lxc-net\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor our user on the host, we need to setup the LXC configuration file. Here, we\u0026rsquo;re going to map the user UID/GIDs into our container.\u003c/p\u003e\n\u003cp\u003eThis set of commands were taken from \u003ca href=\"https://linuxcontainers.org/lxc/getting-started/\"\u003eLXC getting started\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir -p ~/.config/lxc\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecp /etc/lxc/default.conf ~/.config/lxc/default.conf\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMS_UID\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003egrep \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eid -un\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e /etc/subuid  | cut -d : -f 2\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eME_UID\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003egrep \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eid -un\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e /etc/subuid  | cut -d : -f 3\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMS_GID\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003egrep \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eid -un\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e /etc/subgid  | cut -d : -f 2\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eME_GID\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003egrep \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eid -un\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e /etc/subgid  | cut -d : -f 3\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;lxc.idmap = u 0 \u003c/span\u003e$MS_UID\u003cspan style=\"color:#e6db74\"\u003e \u003c/span\u003e$ME_UID\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u0026gt;\u0026gt; ~/.config/lxc/default.conf\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;lxc.idmap = g 0 \u003c/span\u003e$MS_GID\u003cspan style=\"color:#e6db74\"\u003e \u003c/span\u003e$ME_GID\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u0026gt;\u0026gt; ~/.config/lxc/default.conf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow we can download and create our container.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemd-run --unit\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003emy-unit --user --scope -p \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Delegate=yes\u0026#34;\u003c/span\u003e -- lxc-create -t download -n test\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAt the time of writing, we unfortunately have to wrap the \u003ccode\u003elxc-create\u003c/code\u003e command within a \u003ccode\u003esystemd-run\u003c/code\u003e because of a semi-recent change in how \u003ccode\u003ecgroups\u003c/code\u003e work.\u003c/p\u003e\n\u003cp\u003eAfter running that command, you should see a large table of distributions and questions about which one to choose. Of course this is personal preference, but I selected \u003ccode\u003eubuntu jammy amd64\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eOnce downloaded, we can then start our container.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemd-run --unit\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003emy-unit --user --scope -p \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Delegate=yes\u0026#34;\u003c/span\u003e -- lxc-start test\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf we see an error message here, then we can add a log file to check for details.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemd-run --unit\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003emy-unit --user --scope -p \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Delegate=yes\u0026#34;\u003c/span\u003e -- lxc-start test --logfile\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e~/lxc.log\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe error I commonly saw when setting this up was:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003elxc-start test 20231130034412.168 ERROR    start - start.c:print_top_failing_dir:99 - Permission denied - Could not access /home/brandon. Please grant it x access, or add an ACL for the container root\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis means we need to give \u003ccode\u003e$MS_UID\u003c/code\u003e access to open the \u003ccode\u003e/home/brandon\u003c/code\u003e folder. Though what it\u0026rsquo;s really trying to do is access \u003ccode\u003e/home/brandon/.local/share/lxc\u003c/code\u003e for it\u0026rsquo;s \u003ccode\u003erootfs\u003c/code\u003e and \u003ccode\u003econfig\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esetfacl -m u:$MS_UID:x /home/rozek\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen try running the \u003ccode\u003elxc-start\u003c/code\u003e command from before again. Sometimes when setting this up it worked from here, other times it wanted me to add the \u003ccode\u003e+x\u003c/code\u003e permission to other folders.\u003c/p\u003e\n\u003cp\u003eIf you run it and don\u0026rsquo;t see a bunch of errors, then it\u0026rsquo;s hopefully a success! Check that it\u0026rsquo;s running:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003ebrandon@host ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e$ lxc-ls --fancy\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eNAME      STATE   AUTOSTART GROUPS IPV4       IPV6 UNPRIVILEGED \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etest      RUNNING \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e         -      10.0.3.43  -    true   \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can then attach to our container to get a shell within it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elxc-attach test\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf the above command doesn\u0026rsquo;t work, you might need to wrap it in a \u003ccode\u003esystemd-run\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemd-run --unit\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003emy-unit2 --user --scope -p \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Delegate=yes\u0026#34;\u003c/span\u003e -- lxc-attach test\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"auto-starting-lxc-containers-at-boot\"\u003eAuto-starting LXC containers at boot\u003c/h2\u003e\n\u003cp\u003eWhen we start a container with the \u003ccode\u003esystemd-run\u003c/code\u003e command, it\u0026rsquo;s tied to that particular terminal session. If we want to start these containers when the machine boots up, we can rely on \u003ccode\u003esystemd\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFirst let\u0026rsquo;s create a service file under \u003ccode\u003e~/.config/systemd/user/lxc@.service\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eStart LXC container\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAfter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003elxc-net.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWants\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003elxc-net.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eoneshot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDelegate\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eyes\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRemainAfterExit\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eyes\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/lxc-start %i\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStop\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/lxc-stop %i\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eTimeoutStartSec\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edefault.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen we can start our test container\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemctl --user start lxc@test\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eEnable it on bootup\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemctl --user enable lxc@test\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSince this is a user service, we need to make sure linger is on for it to respect\nthe enable on boot setting.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo loginctl enable-linger rozek\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"other-resources\"\u003eOther Resources\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://linuxcontainers.org/lxc/getting-started/\"\u003ehttps://linuxcontainers.org/lxc/getting-started/\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.redhat.com/sysadmin/exploring-containers-lxc\"\u003ehttps://www.redhat.com/sysadmin/exploring-containers-lxc\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://wiki.archlinux.org/title/Linux_Containers\"\u003ehttps://wiki.archlinux.org/title/Linux_Containers\u003c/a\u003e\u003c/p\u003e\n",
    "date_published": "2023.12.03",
    "tags": ["Containers"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/website-badges/",
    "url": "https://brandonrozek.com/blog/website-badges/",
    "title": "Adding badges to my website",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve been coming across more \u003ca href=\"https://neocities.org/\"\u003eneocities\u003c/a\u003e websites recently. It\u0026rsquo;s really cool how many people there hand craft their HTML and CSS to really make a website theirs. One idea I\u0026rsquo;m stealing for my website is adding web badges.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/badges/fedora.gif\" alt=\"Example Badge\"\u003e\u003c/p\u003e\n\u003cp\u003eThese web badges are often small images that shows a product or message. Historically, it dates back to when Netscape in 1996 made the \u003ca href=\"https://web.archive.org/web/20240914044400/https://sillydog.org/netscape/now.html\"\u003eNetscape Now!\u003c/a\u003e badge.\u003c/p\u003e\n\u003cp\u003eSince then, the Geocities picked it up and standardized many of the web badges to have a pixel size of 88x31. These badges are sometimes animated as GIFs, though they usually don\u0026rsquo;t hold dynamic information.\u003c/p\u003e\n\u003cp\u003eIn recent times, we\u0026rsquo;ve seen badges hold dynamic information such as \u003ca href=\"https://github.com/badges/shields\"\u003ecode coverage on GitHub\u003c/a\u003e. Also, Wikipedia has Userboxes that people can add to their User Pages to show dynamic information about themselves. \u003ca href=\"https://en.wikipedia.org/wiki/User:RubenSchade/Userboxes\"\u003eRuben\u0026rsquo;s User Page\u003c/a\u003e showcases many of these.\u003c/p\u003e\n\u003cp\u003eThough to get started with my own website, I decided to look primarily at the traditional 88x31 style web badges. Looking around the web, there are some webpages that have large listings of web badges:\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://web.badges.world/\"\u003ehttps://web.badges.world/\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://cyber.dabamos.de/\"\u003ehttps://cyber.dabamos.de/\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://capstasher.neocities.org/\"\u003ehttps://capstasher.neocities.org/\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eFrom there, we can (1) pick the ones we like (2) download them, and (3) upload them to our webserver. We can then create the \u003ccode\u003eimg\u003c/code\u003e tag in our footer to showcase them.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003eimg\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ewidth\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;88px\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eheight\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;31px\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esrc\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/badges/fedora.gif\u0026#34;\u003c/span\u003e /\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2023.11.26",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/on-earbuds/",
    "url": "https://brandonrozek.com/blog/on-earbuds/",
    "title": "Re: On Earbuds",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn response to \u003ca href=\"https://ricard.blog/other/my-first-noise-cancelling-earbuds/\"\u003eRicard Torres post\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eI also recently bought the Google Pixel buds (August 2023) and agree with you on how magical they are. Beforehand, I primarily used the \u003ca href=\"https://www.sony.com/et/electronics/headband-headphones/wh-1000xm4\"\u003eSony WH-100XM4 headphones\u003c/a\u003e for a couple years. While I still believe that those are superior in terms of audio quality, the Google Pixel buds have it beat in terms of convenience. They allow me to have one earbud in to not only enjoy whichever podcast I\u0026rsquo;m listening to at the moment, but also be able to quickly hear my fiance when she wants to strike a conversation with me.\u003c/p\u003e\n\u003cp\u003eWhile the Google Pixel buds are my new default. I do believe there are situations when other solutions are appropriate.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eFor video calls, I often use a cheap pair of 2.5mm wired earphones. This is mainly to not include latency introduced by bluetooth.\u003c/li\u003e\n\u003cli\u003eWhen I\u0026rsquo;m in an extended noisy non-interactive situation like a airline flight. I find that the Sony WH-100XM4 headphones cancels out more noise which is needed for an enjoyable experience.\u003c/li\u003e\n\u003c/ul\u003e\n",
    "date_published": "2023.11.22",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/mirror-public-repositories/",
    "url": "https://brandonrozek.com/blog/mirror-public-repositories/",
    "title": "Mirror Public Repositories",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eGit is designed to be decentralized. However, many people treat it as a centralized solution by depending on services such as GitHub. What we\u0026rsquo;ve seen through the \u003ca href=\"https://www.zdnet.com/article/riaa-blitz-takes-down-18-github-projects-used-for-downloading-youtube-videos/\"\u003eyoutube-dl debacle\u003c/a\u003e is that repositories that we depend on can be taken down.\u003c/p\u003e\n\u003cp\u003eThis isn\u0026rsquo;t to say that GitHub is evil and that we should move to Bitbucket, Gitlab, Source Hut, etc. But this is more of a commentary on what happens when we depend on one service to host our code. Git is designed to be decentralized, we should make use of that fact!\u003c/p\u003e\n\u003cp\u003eAlso, alleged illegal activity is not the only reason repositories are taken down from the Internet. Sometimes, the \u003ca href=\"https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/\"\u003edeveloper themselves\u003c/a\u003e decide to take it down.\u003c/p\u003e\n\u003cp\u003eAs individuals, we can maintain mirrors of repositories we care about. That way if it ever gets removed from a particular service, we\u0026rsquo;re not out of luck.\u003c/p\u003e\n\u003cp\u003eThe simplest way to go about this is to \u003ccode\u003egit clone\u003c/code\u003e the repositories you care about, and regularly \u003ccode\u003epull\u003c/code\u003e through a cron job or systemd timer. Through systemd maybe it\u0026rsquo;ll look like this:\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e/etc/systemd/system/refresh-hugo.service\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ePull latest changes from Hugo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eoneshot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWorkingDirectory\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/home/brandonrozek/repo/hugo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/git pull\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003e/etc/systemd/system/refresh-hugo.timer\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ePull latest changes from Hugo every week\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRequires\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003erefresh-hugo.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Timer]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eUnit\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003erefresh-hugo.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eOnCalendar\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eweekly\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePersistent\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etimers.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAlternatively, you can use a self-hosted git server instance such as \u003ca href=\"https://forgejo.org/\"\u003eForgejo\u003c/a\u003e to set up pull mirrors through the migration tool. As of the time of writing, this is what I currently do with repositories at \u003ca href=\"https://git.brandonrozek.com/github\"\u003ehttps://git.brandonrozek.com/github\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eI do recommend only mirroring/pulling infrequently such as weekly. We don\u0026rsquo;t want to induce unnecessary load on these centralized services.\u003c/p\u003e\n",
    "date_published": "2023.11.22",
    "tags": ["Git","Archive"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/etc-hosts-connection/",
    "url": "https://brandonrozek.com/blog/etc-hosts-connection/",
    "title": "Changing /etc/hosts based on network connection",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI use my laptop at home, university, and public locations. The IP address I use to connect to a particular resource changes depending on if I\u0026rsquo;m within the network it\u0026rsquo;s hosted on or a VPN. A common solution is to have a DNS server within the VPN that all clients use. This, however, has a few issues:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eIf the client has multiple VPNs connected, only one DNS server can be set.\u003c/li\u003e\n\u003cli\u003eThere may be more latency using a DNS server within a VPN than using a default one provided by the ISP.\u003c/li\u003e\n\u003cli\u003eYou may not have permission to host the DNS server within the VPN network.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eTo address these set of issues, we\u0026rsquo;ll go over how to change \u003ccode\u003e/etc/hosts\u003c/code\u003e on the local client machine depending on which network it\u0026rsquo;s connected to.\u003c/p\u003e\n\u003cp\u003eIn this setup, we\u0026rsquo;ll have a default \u003ccode\u003e/etc/hosts\u003c/code\u003e file. I\u0026rsquo;ll show how to then \u003cem\u003eswap\u003c/em\u003e it with one for a particular connection. To do this, we need a way for a script to run when NetworkManager connects or disconnects from a network.\u003c/p\u003e\n\u003cp\u003eLuckily, \u003ccode\u003eNetworkManager-dispatcher\u003c/code\u003e handles this for us. To get a complete understanding on how to write scripts for this, reference\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eman \u003cspan style=\"color:#ae81ff\"\u003e8\u003c/span\u003e networkmanager-dispatcher\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn essence, scripts within \u003ccode\u003e/etc/NetworkManger/dispatcher.d/\u003c/code\u003e get executed in alphabetical order with two arguments set and a lot of environmental variables.\u003c/p\u003e\n\u003cp\u003eWhat we\u0026rsquo;ll care about in our scripts are:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003e$1\u003c/code\u003e the first argument passed to the script is the interface\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e$2\u003c/code\u003e the second argument refers to the \u003cem\u003eevent\u003c/em\u003e being triggered.\n\u003cul\u003e\n\u003cli\u003ePossible options include: pre-up, up, pre-down, down, vpn-up, vpn-pre-up, vpn-pre-down, vpn-down, hostname, dhcp4-change, dhcp6-change, connectivity-change, reapply.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e$CONNECTION_UUID\u003c/code\u003e refers to a particular connection profile in NetworkManager. This is so we know which \u003ccode\u003e/etc/hosts\u003c/code\u003e file to swap with which connection.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eDoing some quick in dirty tests, I found the following events were triggered when connecting to a particular network:\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e dhcp4-change -\u0026gt; up -\u0026gt; connectivity-change\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eAnd, the following events were triggered when disconnecting from a particular network:\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003econnectivity-change -\u0026gt; down\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eMy first instinct was to use the \u003ccode\u003econnectivity-change\u003c/code\u003e event, however, the \u003ccode\u003eCONNECTION_UUID\u003c/code\u003e variable is not set for those. Instead we\u0026rsquo;ll use the \u003ccode\u003eup/down\u003c/code\u003e events.\u003c/p\u003e\n\u003cp\u003eFor our example, here\u0026rsquo;s what our default \u003ccode\u003e/etc/hosts/\u003c/code\u003e file will look like:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4\n::1         localhost localhost.localdomain localhost6 localhost6.localdomain6\n\n10.10.10.3  home-server.brandonrozek.com\n10.10.10.4  home-desktop.brandonrozek.com\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWhen we\u0026rsquo;re connected to my home network, we\u0026rsquo;ll swap my \u003ccode\u003e/etc/hosts/\u003c/code\u003e to look like:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4\n::1         localhost localhost.localdomain localhost6 localhost6.localdomain6\n\n192.168.0.30  home-server.brandonrozek.com\n192.168.0.40  home-desktop.brandonrozek.com\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe following script we\u0026rsquo;ll store at \u003ccode\u003e/etc/NetworkManager/dispatcher.d/swap_home.sh\u003c/code\u003e which will swap the \u003ccode\u003e/etc/hosts\u003c/code\u003e file with the one stored at \u003ccode\u003e/etc/NetworkManager/hosts.home\u003c/code\u003e when I connect to my home network.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/usr/bin/env bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003einterface\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eevent\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[[\u003c/span\u003e $interface !\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wlp0s20f3\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[[\u003c/span\u003e $event !\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;up\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]]\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        exit \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# This environmental variable is set on UP/DOWN events\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[[\u003c/span\u003e $CONNECTION_UUID !\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;901a1b68-e622-4be6-a61f-a8dc999212b3\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]]\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        exit \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecp /etc/NetworkManager/hosts.home /etc/hosts\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn this script, you might have to replace \u003ccode\u003ewlp0s20f3\u003c/code\u003e to reflect the interface that you\u0026rsquo;re using for connecting to the network. Additionally, you\u0026rsquo;ll have to replace the \u003ccode\u003eCONNECTION_UUID\u003c/code\u003e with the UUID of the connection you\u0026rsquo;re trying to swap under. You can use \u003ccode\u003enmcli c\u003c/code\u003e to show the UUIDs for each of your network connections.\u003c/p\u003e\n\u003cp\u003eSimilarly, when we disconnect from the network, we\u0026rsquo;ll need to set it back to our default \u003ccode\u003e/etc/hosts\u003c/code\u003e file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/usr/bin/env bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003einterface\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eevent\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[[\u003c/span\u003e $interface !\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;wlp0s20f3\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[[\u003c/span\u003e $event !\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;down\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]]\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        exit \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecp /etc/NetworkManager/hosts.default /etc/hosts\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2023.11.22",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/simple-kv-store-sqlite/",
    "url": "https://brandonrozek.com/blog/simple-kv-store-sqlite/",
    "title": "Simple Key-Value Store using Sqlite3",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eA lot of software nowadays are built for scale. You have to setup a Kubernetes cluster and deploy Redis for duplication in order to have a key-value store. Though for many small projects, I feel like it\u0026rsquo;s overkill.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ll show in this post, that we can have a nice simple\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e key-value store using \u003ccode\u003esqlite3\u003c/code\u003e\u003csup id=\"fnref:2\"\u003e\u003ca href=\"#fn:2\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e2\u003c/a\u003e\u003c/sup\u003e. This gives us the benefit that we don\u0026rsquo;t need to use system resources to run a daemon the entire time and only spin up a process when we need it.\u003c/p\u003e\n\u003cp\u003eFor our key-value store, we\u0026rsquo;re going to use a table with two columns:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eA key, which we\u0026rsquo;ll call \u003ccode\u003ename\u003c/code\u003e. This will be a unique \u003ccode\u003eTEXT\u003c/code\u003e type that has to be set.\u003c/li\u003e\n\u003cli\u003eThe value, which we\u0026rsquo;ll call \u003ccode\u003evalue\u003c/code\u003e (Creative, I know.) For our purposes, this will also be a \u003ccode\u003eTEXT\u003c/code\u003e type.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThe SQL to create this table is\u003csup id=\"fnref:3\"\u003e\u003ca href=\"#fn:3\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e3\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eCREATE\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eTABLE\u003c/span\u003e config(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    name TEXT \u003cspan style=\"color:#66d9ef\"\u003eNOT\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNULL\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    value TEXT\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLet\u0026rsquo;s say we want to get the value of the key \u003ccode\u003eauthor\u003c/code\u003e. This is a \u003ccode\u003eSELECT\u003c/code\u003e statement away:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eSELECT\u003c/span\u003e value \u003cspan style=\"color:#66d9ef\"\u003eFROM\u003c/span\u003e config \u003cspan style=\"color:#66d9ef\"\u003ewhere\u003c/span\u003e name\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;author\u0026#39;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow let\u0026rsquo;s say that we want to insert a new key into the table.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eINSERT\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eINTO\u003c/span\u003e config(name, value) \u003cspan style=\"color:#66d9ef\"\u003eVALUES\u003c/span\u003e (\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;a\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;1\u0026#39;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhat about updating?\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eUPDATE\u003c/span\u003e config \u003cspan style=\"color:#66d9ef\"\u003eSET\u003c/span\u003e value\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;2\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eWHERE\u003c/span\u003e name\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;a\u0026#39;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe tricky part is if we want to insert if the key does not exist, and update if it does. To handle this we\u0026rsquo;ll need to resolve the \u003ca href=\"https://www.sqlite.org/lang_conflict.html\"\u003econflict\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eINSERT\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eINTO\u003c/span\u003e config(name, value) \u003cspan style=\"color:#66d9ef\"\u003eVALUES\u003c/span\u003e (\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;a\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;3\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#66d9ef\"\u003eON\u003c/span\u003e CONFLICT(name) \u003cspan style=\"color:#66d9ef\"\u003eDO\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eUPDATE\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eSET\u003c/span\u003e value\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eexcluded.value;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLastly if you want to export the entire key-value store as a CSV:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esqlite3 -header -csv data.db \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;SELECT * FROM config;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis is nice and all, but it\u0026rsquo;s inconvinient to type out all these SQL commands. Therefore, I wrote two little bash scripts.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ccode\u003esqlite3_getkv\u003c/code\u003e\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/sh\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o errexit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o nounset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o pipefail\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eshow_usage\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Usage: sqlite3_getkv [db_file] [key]\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check argument count\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$#\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -ne \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    show_usage\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Initalize database file is not already\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esqlite3 \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;CREATE TABLE IF NOT EXISTS config(name TEXT NOT NULL UNIQUE, value TEXT);\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Get value from key\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esqlite3 \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;SELECT value FROM CONFIG where name=\u0026#39;\u003c/span\u003e$2\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003e\u003ccode\u003essqlite3_setkv\u003c/code\u003e\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/sh\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o errexit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o nounset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o pipefail\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eshow_usage\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Usage: sqlite3_setkv [db_file] [key] [value]\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check argument count\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$#\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -ne \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    show_usage\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Initalize database file is not already\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esqlite3 \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;CREATE TABLE IF NOT EXISTS config(name TEXT NOT NULL UNIQUE, value TEXT);\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Set key-value pair\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esqlite3 \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;INSERT INTO config(name, value) VALUES (\u0026#39;\u003c/span\u003e$2\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;, \u0026#39;\u003c/span\u003e$3\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;) ON CONFLICT(name) DO UPDATE SET value=excluded.value;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eExample Usage:\u003c/strong\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ ./sqlite3_setkv.sh test.db a 4\n$ ./sqlite3_setkv.sh test.db c 5\n$ ./sqlite3_getkv.sh test.db a\n4\n$ ./sqlite3_setkv.sh test.db a 5\n$ ./sqlite3_getkv.sh test.db a\n5\n\u003c/code\u003e\u003c/pre\u003e\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eSomehow my idea of easier, simpler, and more maintainable is writing bash scripts.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:2\"\u003e\n\u003cp\u003eJustin pointed out that the \u003ca href=\"https://github.com/python/cpython/blob/a15a584bf3f94ea11ab9363548c8872251364000/Lib/dbm/sqlite3.py#L7\"\u003eCPython implementation\u003c/a\u003e works similarly.\u0026#160;\u003ca href=\"#fnref:2\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:3\"\u003e\n\u003cp\u003eUnfortantely, we can\u0026rsquo;t only use the \u003ccode\u003ePRIMARY KEY\u003c/code\u003e qualifier for the name field as sqlite has a \u003ca href=\"https://www.sqlite.org/quirks.html\"\u003ehistorical quirk\u003c/a\u003e which allows primary keys to be null.\u0026#160;\u003ca href=\"#fnref:3\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2023.11.09",
    "tags": ["DB"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/plex-ram-transcode/",
    "url": "https://brandonrozek.com/blog/plex-ram-transcode/",
    "title": "Transcoding Plex Video in RAM",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOne not so secret trick I recently learned is that you can have Plex transcode video in RAM to speed it up. This is especially useful if like me, your server runs on spinning rust hard drives and you have many GBs of RAM to spare.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eStep 1:\u003c/strong\u003e Set a transcode location in Plex.\u003c/p\u003e\n\u003cp\u003eLog into your admin account. Then go to Settings -\u0026gt; Transcoder.\u003c/p\u003e\n\u003cp\u003eFrom there select the \u0026ldquo;Show Advanced\u0026rdquo; button and scroll down to the \u0026ldquo;Set Transcoder temporary directory\u0026rdquo;.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve set it to \u003ccode\u003e/transcode\u003c/code\u003e and then hit the Save button.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eStep 2: Setup \u003ccode\u003etmpfs\u003c/code\u003e\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eWe can use \u003ccode\u003etmpfs\u003c/code\u003e to setup a filesystem that will be stored in RAM at the transcode directory. If you have Plex installed not in a container, then you can follow the \u003ca href=\"https://wiki.archlinux.org/title/Tmpfs\"\u003eArch Wiki Instructions\u003c/a\u003e for setting it up.\u003c/p\u003e\n\u003cp\u003eI use \u003ccode\u003edocker-compose\u003c/code\u003e for my setup, so it involved adding some limes to my yaml.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eplex\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eimage\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003elscr.io/linuxserver/plex:1.32.5\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# ...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003etmpfs\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003e/transcode\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# ...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI don\u0026rsquo;t promise massive performance increase by setting up Plex this way, but I do think it makes sense:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eImproved write speeds for the transcoded files\u003c/li\u003e\n\u003cli\u003eThe transcoded files aren\u0026rsquo;t persistent\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIf you\u0026rsquo;re of a different opinion, let me know.\u003c/p\u003e\n",
    "date_published": "2023.11.09",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/ahead-behind-git/",
    "url": "https://brandonrozek.com/blog/ahead-behind-git/",
    "title": "Figuring out which git repositories are ahead or behind",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eMore often than I care to admit, I would pick up to do work on a device only to realize that I\u0026rsquo;m working with an older version of the codebase. I could use the \u003ccode\u003egit status\u003c/code\u003e command, but the output is verbose and stale if you haven\u0026rsquo;t \u003ccode\u003egit fetch/pull\u003c/code\u003e\u0026rsquo;d.\u003c/p\u003e\n\u003cp\u003eI keep the majority of my git repositories in the folder \u003ccode\u003e~/repo/\u003c/code\u003e on all my devices. Inspired by a recent \u003ca href=\"https://claytonerrington.com/blog/git-status/\"\u003eblog post by Clayton Errington\u003c/a\u003e, I wanted a way to quickly check within a folder which repositories need updating.  Their blog post has a script written in PowerShell. I decided to write my own bash implementation, and also ignore the bit about modified files since I mostly care about the state of my commits with respect to the \u003ccode\u003eorigin\u003c/code\u003e remote.\u003c/p\u003e\n\u003cp\u003eBefore writing a recursive implementation, let\u0026rsquo;s first discuss how to check the ahead/behind status for a single repository.\u003c/p\u003e\n\u003cp\u003eFirst things first, you need to make sure that we have all the references from the remote.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit remote update\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo print out how many commits the local \u003ccode\u003emain\u003c/code\u003e branch is ahead of the one located on the \u003ccode\u003eorigin\u003c/code\u003e remote we can use:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit rev-list --count origin/main..main\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSimilarly for checking how many commits the local \u003ccode\u003emain\u003c/code\u003e branch is behind we can use:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit rev-list --count main..origin/main\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eInstead of looking at the \u003ccode\u003emain\u003c/code\u003e branch, maybe we can to check whichever branch we\u0026rsquo;re currently at.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebranch\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003egit rev-parse --abbrev-ref HEAD\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can wrap all of this into a nice bash function. We\u0026rsquo;ll additionally check if there is a \u003ccode\u003e.git\u003c/code\u003e in the current folder as none of the git commands will work without it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echeck_remote\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e -d .git \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    git remote update \u0026gt; /dev/null 2\u0026gt; /dev/null\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    branch\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003egit rev-parse --abbrev-ref HEAD\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ahead\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003egit rev-list --count origin/$branch..$branch\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    behind\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003egit rev-list --count $branch..origin/$branch\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$ahead\u003cspan style=\"color:#e6db74\"\u003e commits ahead, \u003c/span\u003e$behind\u003cspan style=\"color:#e6db74\"\u003e commits behind\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI currently have 15 repositories in my \u003ccode\u003e~/repo\u003c/code\u003e folder. Now I can \u003ccode\u003ecd\u003c/code\u003e into each of them and run this bash function. Or, I can have \u003ccode\u003ebash\u003c/code\u003e do it for me :)\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s write a function called \u003ccode\u003eprocess\u003c/code\u003e that does just that. We\u0026rsquo;ll pass in a folder as an argument stored in \u003ccode\u003e$1\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprocess\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e -d \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e/.git\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    pushd \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$PWD\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u0026gt; /dev/null\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    cd \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo -n \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    check_remote\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    popd \u0026gt; /dev/null\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003epushd\u003c/code\u003e command will keep track of the folder that we\u0026rsquo;re currently in. Then we \u003ccode\u003ecd\u003c/code\u003e into the directory that has \u003ccode\u003e.git\u003c/code\u003e folder. Print the name of the folder so we can associate the ahead/behind counts, and then run the \u003ccode\u003echeck_remote\u003c/code\u003e function. Lastly we \u003ccode\u003epopd\u003c/code\u003e back to the folder we started from.\u003c/p\u003e\n\u003cp\u003eAll that\u0026rsquo;s left is to get the list of folders to process:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efind . -type d -print0\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFeed it into a \u003ccode\u003ewhile read\u003c/code\u003e loop passing in each folder into the \u003ccode\u003eprocess\u003c/code\u003e function.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efind . -type d -print0 | \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e read -d \u003cspan style=\"color:#e6db74\"\u003e$\u0026#39;\\0\u0026#39;\u003c/span\u003e folder\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  process $folder\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAll together the script will look like:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o errexit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o nounset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o pipefail\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eshow_usage\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Usage: git-remote-status [-R]\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echeck_remote\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e -d .git \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    git remote update \u0026gt; /dev/null 2\u0026gt; /dev/null\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    branch\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003egit rev-parse --abbrev-ref HEAD\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ahead\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003egit rev-list --count origin/$branch..$branch\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    behind\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003egit rev-list --count $branch..origin/$branch\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$ahead\u003cspan style=\"color:#e6db74\"\u003e commits ahead, \u003c/span\u003e$behind\u003cspan style=\"color:#e6db74\"\u003e commits behind\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$#\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -eq \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  check_remote\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  exit \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e !\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;-R\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  show_usage\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprocess\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e -d \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e/.git\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    pushd \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$PWD\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u0026gt; /dev/null\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    cd \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo -n \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    check_remote\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    popd \u0026gt; /dev/null\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport -f process\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efind . -type d -print0 | \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e read -d \u003cspan style=\"color:#e6db74\"\u003e$\u0026#39;\\0\u0026#39;\u003c/span\u003e folder\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  process $folder\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis gives us two options. If we pass in no flags, then it\u0026rsquo;ll print out the ahead/behind status for the current folder. If we pass in \u003ccode\u003e-R\u003c/code\u003e, then we recursively check all the subfolders as well.\u003c/p\u003e\n\u003cp\u003eExample Output of \u003ccode\u003egit-remote-status -R\u003c/code\u003e:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e./project1 0 commits ahead, 3 commits behind\n./project2 1 commits ahead, 0 commits behind\n./project3 1 commits ahead, 2 commits behind\n./project4 0 commits ahead, 0 commits behind\n./project5 0 commits ahead, 0 commits behind\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2023.11.09",
    "tags": ["Git"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/test-mqtt/",
    "url": "https://brandonrozek.com/blog/test-mqtt/",
    "title": "Quickly configuring and testing the Mosquitto MQTT broker",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve been playing with Tasmota smart devices recently; and to hook them up with home assistant, they both need to be configured to utilize MQTT. In this post, we\u0026rsquo;ll only focus on the MQTT part. From quickly installing it to making sure publish/subscribe messages work on another machine.\u003c/p\u003e\n\u003ch2 id=\"installing-mqtt-broker\"\u003eInstalling MQTT Broker\u003c/h2\u003e\n\u003cp\u003eOn the server you want to install MQTT on:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dnf install mosquitto\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe won\u0026rsquo;t go over how to setup TLS or authenticated users. Instead for a quick test, we\u0026rsquo;ll configure it to allow for anonymous connections over any interface.\u003c/p\u003e\n\u003cp\u003eAdd the following lines to \u003ccode\u003e/etc/mosquitto/mosquitto.conf\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003elistener 1883\nallow_anonymous true\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThen enable the systemd service to start mosquitto\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl enable --now mosquitto\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eMake sure it\u0026rsquo;s running:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl status mosquitto\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"testing-the-broker\"\u003eTesting the Broker\u003c/h2\u003e\n\u003cp\u003eMost tutorials have you test the publish/subscribe on the local machine. Though given we\u0026rsquo;re working with smart devices, we need to make sure it works on another machine first.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eApproach 1:\u003c/strong\u003e Install the mosquitto tools on that machine. Then you\u0026rsquo;ll need to open two terminals.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eOn terminal 1, subscribe to all messages \u003ccode\u003emosquitto_sub -h [MQTT_BROKER_ADDR] -t \u0026quot;#\u0026quot; \u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eOn terminal 2, publish a message: \u003ccode\u003emosquitto_pub -h [MQTT_BROKER_ADDR] -t \u0026quot;test\u0026quot;  -m \u0026quot;Hello, World\u0026quot;\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eOn the first terminal, you should see the string \u003ccode\u003eHello, World\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eApproach 2:\u003c/strong\u003e Use curl.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eOn terminal 1, subscribe to all messages with \u003ccode\u003ecurl mqtt://[MQTT_BROKER_ADDR]/%23 --OUTPUT - --trace -\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eOn terminal 2, publish a message \u003ccode\u003ecurl -d 'Hello, World'  mqtt://[MQTT_BROKER_ADDR]/test/\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eSimilarly, you should see the string \u003ccode\u003eHello, World\u003c/code\u003e along with a bunch of debugging information.\u003c/p\u003e\n",
    "date_published": "2023.10.30",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/emoji-kitchen/",
    "url": "https://brandonrozek.com/blog/emoji-kitchen/",
    "title": "Emoji Kitchen: Combinations of Emojis",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eClare mentioned to me today that Google keyboard has combination emojis. Intrigued, I checked it out. Not all emojis are supported, but a lot of the facial expression ones are. Select a supported emoji, and you\u0026rsquo;ll be presented with different combination results.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20231020000035.jpg\" alt=\"Image of Google Keyboard\"\u003e\u003c/p\u003e\n\u003cp\u003eGoogle calls this \u003cem\u003eEmoji Kitchen\u003c/em\u003e. \u003ca href=\"https://xsalazar.com/\"\u003eXavier Salazar\u003c/a\u003e created a \u003ca href=\"https://emojikitchen.dev/\"\u003enice website to visualize all the combinations\u003c/a\u003e.   These emojis are not part of any text encoding standard, but instead are image files.\u003c/p\u003e\n\u003cp\u003eNow it\u0026rsquo;s too hard for me to keep track of all these emojis. However, if there\u0026rsquo;s one series that particularly resonates with me. I would say it\u0026rsquo;s the turtle. Just look how awesome he is.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/turtles.webp\" alt=\"12 Turtle Emojis\"\u003e\u003c/p\u003e\n",
    "date_published": "2023.10.19",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/obtaining-ipv6-address-hurricane-electric/",
    "url": "https://brandonrozek.com/blog/obtaining-ipv6-address-hurricane-electric/",
    "title": "Obtaining a IPv6 Address via Hurricane Electric's Tunnel Broker Service",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLately, I have come across a virtual private server that only has a IPv4 address and not a IPv6 address. Since all regional Internet registries have \u003ca href=\"https://en.wikipedia.org/wiki/IPv4_address_exhaustion\"\u003eexhausted their IPv4 address pools since 2019\u003c/a\u003e, I wanted to find a way to route IPv6 traffic to that server.\u003c/p\u003e\n\u003cp\u003eLuckily, Hurricane Electric provides a free IPv6 tunnel broker service. The \u003ca href=\"https://wiki.archlinux.org/title/IPv6_tunnel_broker_setup\"\u003eArch Linux Wiki\u003c/a\u003e has a great guide on how to set it up. This post will start off with setting up the service, and we\u0026rsquo;ll discuss a couple bits of consideration we need to take to get IPv6 traffic to a webserver running in a docker/podman container.\u003c/p\u003e\n\u003cp\u003eHurricane Electric is a Tier 2 Internet service provider. This means that they \u003ca href=\"https://en.wikipedia.org/wiki/Peering\"\u003epeer\u003c/a\u003e with other Internet service providers to route network traffic with. Since at \u003ca href=\"https://forums.he.net/index.php?topic=783.0\"\u003eleast 2010\u003c/a\u003e, Hurricane Electric has offered a \u003ca href=\"https://www.tunnelbroker.net/\"\u003efree IPv6 tunnel\u003c/a\u003e.   This gives me confidence that they should at least continue to offer this service in the near future. In order to route IPv6 traffic to your IPv4 server, they make use of the the \u003ca href=\"https://en.wikipedia.org/wiki/6in4\"\u003e6in4 tunneling protocol\u003c/a\u003e. This encapsulates the IPv6 packet with a IPv4 header.\u003c/p\u003e\n\u003cp\u003eTo get setup, create on account on \u003ca href=\"https://tunnelbroker.net\"\u003ehttps://tunnelbroker.net\u003c/a\u003e. After verifying your email\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e, login and click on \u0026ldquo;Create Regular Tunnel\u0026rdquo;. From there it will ask you for the IPv4 address of your server. Insert that and then click the tunnel server that is the closest geographically to where the server is located.\u003c/p\u003e\n\u003cp\u003eOnce the tunnel is created, you\u0026rsquo;ll need to take note of four addresses within the \u0026ldquo;IPv6 Tunnel Endpoints\u0026rdquo; section:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eServer IPv4 Address\u003c/li\u003e\n\u003cli\u003eServer IPv6 Address\u003c/li\u003e\n\u003cli\u003eClient IPv4 Address\u003c/li\u003e\n\u003cli\u003eClient IPv6 Address\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIn terms of configuring the networking on the server, I found the systemd script on the Arch Wiki the most straightforward setup. Within \u003ccode\u003e/etc/systemd/system/he-ipv6.service\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ehe.net IPv6 tunnel\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAfter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enetwork.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eoneshot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRemainAfterExit\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eyes\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/ip tunnel add he-ipv6 mode sit remote server_IPv4_address local client_IPv4_address ttl 255\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/ip link set he-ipv6 up mtu 1480\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/ip addr add client_IPv6_address dev he-ipv6\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/ip -6 route add ::/0 dev he-ipv6\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStop\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/ip -6 route del ::/0 dev he-ipv6\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStop\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/ip link set he-ipv6 down\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStop\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/ip tunnel del he-ipv6\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen enable/start the service with:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl enable --now he-ipv6.service\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWith this, your server should now be IPv6 routable. You can test this by trying to ping your server from another IPv6 enabled device.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eping client_ipv6_address\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf that works, next you\u0026rsquo;ll have to make sure that \u003ca href=\"https://brandonrozek.com/blog/podman-nginx-tcpv6-http2-ready/\"\u003edocker/podman and nginx are configured to accept TCPv6 traffic\u003c/a\u003e. Finally, since the tunnel is on a different network interface, you may need to allow routing at the firewall level.\u003c/p\u003e\n\u003cp\u003eFor example, to allow from traffic between the podman and tunnel interfaces:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo ufw route allow in on he-ipv6 out on podman1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo ufw route allow in on podman1 out on he-ipv6\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eA reader reached out to let me know that Hurricane Electric does not\nsupport @outlook.com or @gmail.com addresses for the email.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2023.10.09",
    "tags": ["Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/hiding-answers-lecture-slides-latex-beamer/",
    "url": "https://brandonrozek.com/blog/hiding-answers-lecture-slides-latex-beamer/",
    "title": "Hiding Answers in Lecture Slides using Latex Beamer",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eAsking questions is a great way to try to solicitate engagement from students. However, it can feel at odds with trying to provide a fully comprehensive slide deck at times. Similar to having notes \u003ca href=\"/blog/notes-beamer-latex/\"\u003ehidden for yourself\u003c/a\u003e, we should also have a way to hide answers for the students of the present but not the students of the future.\u003c/p\u003e\n\u003cp\u003eLaTex beamer has the \u003ccode\u003e\\invisible\u003c/code\u003e command which holds the space for the text but doesn\u0026rsquo;t display it. We can create a new command with an if statement stating whether or not we want to use \u003ccode\u003e\\invisible\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFor this, I called the variable \u003ccode\u003eshowanswer\u003c/code\u003e, but you can call it whatever you like. Put the following code in the preamble of your beamer document.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\newif\\ifshowanswer\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e%\\showanswertrue % Uncomment when you want to show the answer\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\newcommand\u003c/span\u003e{\u003cspan style=\"color:#66d9ef\"\u003e\\HideAns\u003c/span\u003e}[1]{\u003cspan style=\"color:#66d9ef\"\u003e\\unless\\ifshowanswer\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e\\invisible\u003c/span\u003e{#1} \u003cspan style=\"color:#66d9ef\"\u003e\\else\u003c/span\u003e #1 \u003cspan style=\"color:#66d9ef\"\u003e\\fi\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo write \u003ccode\u003eif not\u003c/code\u003e in LaTex, we need to use \u003ccode\u003e\\unless\\if\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eNow let\u0026rsquo;s see this in action. Here is a slide I gave in a recent lecture about Unification:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{frame}{Examples}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{enumerate}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e$\u003c/span\u003ef\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ex, y\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \\eq f\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eg\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ex\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e, b\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e$\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{itemize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e Answer: \u003cspan style=\"color:#66d9ef\"\u003e\\HideAns\u003c/span\u003e{None - Occurs Check}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{itemize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e$\u003c/span\u003ef\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ex, a\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \\eq f\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ea, b\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e$\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{itemize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e Answer: \u003cspan style=\"color:#66d9ef\"\u003e\\HideAns\u003c/span\u003e{None - Function Clash}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{itemize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e$\u003c/span\u003eg\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ex\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \\eq g\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ey\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e$\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{itemize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e Answer: \u003cspan style=\"color:#66d9ef\"\u003e\\HideAns\u003c/span\u003e{\u003cspan style=\"color:#e6db74\"\u003e$\u003c/span\u003ex \\mapsto y\u003cspan style=\"color:#e6db74\"\u003e$\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{itemize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{enumerate}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{frame}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe left shows the hidden version, and the right shows the version I can distribute afterwards.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20231009120041.png\" alt=\"Screenshot showing example with and without hidden answers\"\u003e\u003c/p\u003e\n\u003cp\u003eMinimal Working Example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\documentclass\u003c/span\u003e{beamer}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usetheme\u003c/span\u003e{Copenhagen}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usepackage\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[utf8]\u003c/span\u003e{inputenc}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\newcommand\u003c/span\u003e{\u003cspan style=\"color:#66d9ef\"\u003e\\eq\u003c/span\u003e}{\u003cspan style=\"color:#66d9ef\"\u003e\\stackrel\u003c/span\u003e{?}{=}}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\newif\\ifshowanswer\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e% \\showanswertrue % Uncomment when you want to show the answer\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\newcommand\u003c/span\u003e{\u003cspan style=\"color:#66d9ef\"\u003e\\HideAns\u003c/span\u003e}[1]{\u003cspan style=\"color:#66d9ef\"\u003e\\unless\\ifshowanswer\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e\\invisible\u003c/span\u003e{#1} \u003cspan style=\"color:#66d9ef\"\u003e\\else\u003c/span\u003e #1 \u003cspan style=\"color:#66d9ef\"\u003e\\fi\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{document}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{frame}{Examples}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{enumerate}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e$\u003c/span\u003ef\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ex, y\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \\eq f\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eg\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ex\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e, b\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e$\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{itemize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e Answer: \u003cspan style=\"color:#66d9ef\"\u003e\\HideAns\u003c/span\u003e{None - Occurs Check}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{itemize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e$\u003c/span\u003ef\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ex, a\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \\eq f\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ea, b\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e$\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{itemize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e Answer: \u003cspan style=\"color:#66d9ef\"\u003e\\HideAns\u003c/span\u003e{None - Function Clash}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{itemize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e$\u003c/span\u003eg\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ex\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \\eq g\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ey\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e$\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{itemize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e Answer: \u003cspan style=\"color:#66d9ef\"\u003e\\HideAns\u003c/span\u003e{\u003cspan style=\"color:#e6db74\"\u003e$\u003c/span\u003ex \\mapsto y\u003cspan style=\"color:#e6db74\"\u003e$\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{itemize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{enumerate}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{frame}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{document}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2023.10.09",
    "tags": ["LaTex"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/block-storage-application-data/",
    "url": "https://brandonrozek.com/blog/block-storage-application-data/",
    "title": "Use block storage for your application data",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOne day I was upgrading my server from Fedora 37 to 38 and the thought came to my head.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eWhat if this installation fails and I need to recreate the server?\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eLuckily that didn\u0026rsquo;t come to pass, but the thought was enough to scare me into learning \u003ca href=\"https://www.ansible.com/\"\u003eAnsible\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eHonestly, I\u0026rsquo;ve been skeptical of it for a while. I thought that having bash automation scripts was good enough and that Ansible will come and go.\u003c/p\u003e\n\u003cp\u003eExcept, that it\u0026rsquo;s been around for the last 11 years\u0026hellip;\u003c/p\u003e\n\u003cp\u003eConsider me converted! My favorite module is \u003ca href=\"https://docs.ansible.com/ansible/latest/collections/ansible/builtin/lineinfile_module.html\"\u003e\u003ccode\u003elineinfile\u003c/code\u003e\u003c/a\u003e. It\u0026rsquo;s all too common in Linux for setup instructions to contain \u0026ldquo;edit x line in y file.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eNow, I have Playbooks written for configuring my server, but what about the data?\u003c/p\u003e\n\u003cp\u003eI was chatting with my good friend Stefano Coronado and he suggested using block volumes. Most VPS providers offer this feature and its persistent data storage that can be attached to one or more servers within the same data center.\u003c/p\u003e\n\u003cp\u003eThis of course does not substitute having good backups, but having separation between configuration and data is a great idea.\u003c/p\u003e\n\u003cp\u003eLets say that I have block storage mounted on \u003ccode\u003e/mnt/storage\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s easy to point directly to it within docker-compose:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yml\" data-lang=\"yml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003edatabase\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eimage\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003edocker.io/mongo:5.0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003evolumes\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003e/mnt/storage/database/data/db:/data/db\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you\u0026rsquo;re using an application outside of docker and is not easily configurable, we can make use of symlinks.\u003c/p\u003e\n\u003cp\u003eFor example, if an application is expecting configuration to be at \u003ccode\u003e/etc/special\u003c/code\u003e then:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo ln -s /mnt/storage/special /etc/special\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003esets up a symlink at \u003ccode\u003e/etc/special\u003c/code\u003e to point at \u003ccode\u003e/mnt/storage/special\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eLearn more on how to specifically setup and mount a block volume depending on your hosting provider:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://www.linode.com/docs/products/storage/block-storage/\"\u003eLinode\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.digitalocean.com/products/volumes/\"\u003eDigitalOcean\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://www.vultr.com/docs/block-storage\"\u003eVultr\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://aws.amazon.com/ebs/\"\u003eAWS\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n",
    "date_published": "2023.10.02",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/private-syncthing-network/",
    "url": "https://brandonrozek.com/blog/private-syncthing-network/",
    "title": "How to set up a private Syncthing network",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://syncthing.net/\"\u003eSyncthing\u003c/a\u003e is a peer to peer file synchronization program that I\u0026rsquo;ve been using for many years. I\u0026rsquo;ve never had any issues with it and I consider it rock solid in stability.\u003c/p\u003e\n\u003cp\u003eEach device is given a unique identifier, which it can then query a discovery server for its IP address. The Syncthing foundation runs a public discovery server to facilitate this.\u003c/p\u003e\n\u003cp\u003eOnce the IPs are known, if a direct connection can\u0026rsquo;t be established, then Syncthing will make use of a relay. A relay only forwards encrypted packets like a router and is unable to inspect its contents.\u003c/p\u003e\n\u003cp\u003eThe community runs a pool of public relay servers that are configured by default as well.\u003c/p\u003e\n\u003cp\u003eFor privacy reasons, one might not want to make use of these public discovery and relay servers. Also, it may be more performant to run your own relay servers. We\u0026rsquo;ll show how you can configure Syncthing to work without relying on these community services.\u003c/p\u003e\n\u003cp\u003eWe\u0026rsquo;ll go over:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eHow to setup a private relay server\u003c/li\u003e\n\u003cli\u003eHow to configure the clients to disable discovery and use the private relay server.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"setting-up-the-private-relay-server\"\u003eSetting up the private relay server\u003c/h3\u003e\n\u003cp\u003eFirst we\u0026rsquo;ll need to have \u003ccode\u003estrelaysrv\u003c/code\u003e installed. On Fedora, this is under the \u003ccode\u003esyncthing-tools\u003c/code\u003e package.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dnf install syncthing-tools\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen we need to make sure that port 22067 is open to TCP traffic on our firewall. Optionally, strelaysrv can expose a \u003ccode\u003e/status\u003c/code\u003e HTTP endpoint on port 22070, but I didn\u0026rsquo;t enable it myself.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo ufw allow 22067/tcp\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf applicable, make sure that these are enabled in your cloud firewall.\u003c/p\u003e\n\u003cp\u003eWhen \u003ccode\u003estrelaysrv\u003c/code\u003e runs for the first time, it\u0026rsquo;s going to create a set of public/private keys. We\u0026rsquo;ll store this in \u003ccode\u003e/etc/strelaysrv\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mkdir /etc/strelaysrv\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo run as a daemon on startup, we\u0026rsquo;ll write a systemd service to \u003ccode\u003e/etc/systemd/system/strelaysrv.service\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eSyncthing Relay Server\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAfter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enetwork.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDocumentation\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eman:strelaysrv(1)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/strelaysrv -keys=/etc/strelaysrv -status-srv=\u0026#34;\u0026#34; -token=INSERT_TOKEN_HERE\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Hardening\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eUser\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ebrandonrozek\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eGroup\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ebrandonrozek\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eProtectSystem\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003estrict\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eReadWritePaths\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/etc/strelaysrv\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eNoNewPrivileges\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePrivateTmp\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePrivateDevices\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eProtectHome\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eSystemCallArchitectures\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enative\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eMemoryDenyWriteExecute\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLet\u0026rsquo;s discuss the flags given in \u003ccode\u003eExecStart\u003c/code\u003e:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003e-keys\u003c/code\u003e indicate the location that the public/private keys will be written to or read from\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e-status-srv=\u0026quot;\u0026quot;\u003c/code\u003e disables the status endpoint on port 22070. You can remove that flag if you want that feature.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e-token=\u0026quot;INSERT_TOKEN_HERE\u0026quot;\u003c/code\u003e first prevents the server from joining the public pool and requires clients to specify the token in order to use the relay.\n\u003cul\u003e\n\u003cli\u003eNOTE:  Replace the token with a random output, such as \u003ccode\u003euuidgen\u003c/code\u003e.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThe hardening section is better left to a future blog post. In the meantime, read the \u003ca href=\"https://www.freedesktop.org/software/systemd/man/systemd.exec.html\"\u003eFreedesktop documentation\u003c/a\u003e for more information.\u003c/p\u003e\n\u003cp\u003eNow we can enable the service on boot and start the Syncthing relay service.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl enable --now strelaysrv.service\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou\u0026rsquo;ll need to note the \u003ccode\u003erelay://\u003c/code\u003e URL shown at \u003ccode\u003esudo systemctl status strelaysrv\u003c/code\u003e for the next section.\u003c/p\u003e\n\u003ch3 id=\"client-configuration\"\u003eClient Configuration\u003c/h3\u003e\n\u003cp\u003eOn the client side, we have to prevent the Syncthing service from listening to traffic from public relays and not rely on the public servers for discovery. These instructions are for the GUI interface on \u003ccode\u003elocalhost:8384\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eLets say we have a relay at \u003ccode\u003erelay:examplerelay.com?id=SOME-ID\u0026amp;token=SOME-TOKEN\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eChange Settings-\u0026gt;Connections-\u0026gt;Sync Protocol Listen from \u003ccode\u003edefault\u003c/code\u003e to:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003erelay:examplerelay.com:22067?id=SOME-ID\u0026amp;token=SOME-TOKEN\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIf we want to allow for direct connections, then we\u0026rsquo;ll need to have port \u003ccode\u003e22000\u003c/code\u003e/UDP open on the client side.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo ufw allow 22000/udp\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen edit Settings-\u0026gt;Connections-\u0026gt;Sync Protocol Listen to:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003equic://0.0.0.0:22000, relay://examplerelay.com:22067/?id=SOME-ID\u0026amp;token=SOME-TOKEN\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eUncheck \u0026lsquo;Global Discovery\u0026rsquo; within that menu. Optionally,  uncheck \u0026ldquo;Local Discovery\u0026rdquo; as well.\u003c/p\u003e\n\u003cp\u003eLocal discovery only sends packets within the local network so it\u0026rsquo;s safe to leave it enabled if desired. If you\u0026rsquo;re worried about information leakage in a public WiFi network, then leave it disabled.\u003c/p\u003e\n\u003cp\u003eSince we disabled the discovery server, how do we find a connection to the different devices? Here, we can make use of the relay, and/or hard code static ip addresses that some devices may have.\u003c/p\u003e\n\u003cp\u003eFor example, say that I have a home-server on the following IP addresses:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003e192.168.1.58\u003c/code\u003e within my home network\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e10.10.2.7\u003c/code\u003e within my Wireguard network.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThen under home-server-\u0026gt;Settings-\u0026gt;Advanced-\u0026gt;Addresses, I can set it to:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003equic://192.168.1.58, quic:10.10.2.7, relay://examplerelay.com:22067\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis will try to establish a direct connection using the local and wireguard IP addresses and fallback to the relay if not possible.\u003c/p\u003e\n\u003ch3 id=\"conclusion\"\u003eConclusion\u003c/h3\u003e\n\u003cp\u003eWith these configuration changes, Syncthing no longer relies on public discovery and relay servers and instead makes use of your own relay server.\u003c/p\u003e\n\u003cp\u003eThis helps improve privacy and perhaps even efficiency as well! If you run into any troubles during this configuration let me know and I can expand upon this post.\u003c/p\u003e\n",
    "date_published": "2023.09.19",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/day-in-brooklyn/",
    "url": "https://brandonrozek.com/blog/day-in-brooklyn/",
    "title": "A day in Brooklyn",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLast month, my brother came to visit me. I was staying at NYC at the time, and took this as a perfect opportunity to explore around. The target on Saturday, Brooklyn.\u003c/p\u003e\n\u003cp\u003eBeginning from April through Octoboer, \u003ca href=\"https://www.smorgasburg.com/\"\u003eSmorgasburg\u003c/a\u003e Williamsburg offers a variety of different food stalls starting in the morning. We decided to start the day there with a late breakfast.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20230805161016.jpg\" alt=\"Image of many tents and people at Smorgasburg Williamsburg\"\u003e\u003c/p\u003e\n\u003cp\u003eWe had some \u003ca href=\"https://www.mycbao.com/\"\u003esteamed bao buns\u003c/a\u003e (bun buns), drank \u003ca href=\"https://johnsjuice.com/\"\u003ejuice out of a hallowed out pineapple\u003c/a\u003e, and ate \u003ca href=\"https://www.instagram.com/ume.nyc/?hl=en\"\u003etaiyaki\u003c/a\u003e. There wasn\u0026rsquo;t a lot of shaded seating available, so we ended up eating on the grass near a tree.\u003c/p\u003e\n\u003cp\u003eAfterwards, we walked around Williamsburg and visited a few shops.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20230805172226.jpg\" alt=\"Image of a indoor shopping area\"\u003e\u003c/p\u003e\n\u003cp\u003eWe then took the train to \u0026ldquo;Down Under the \u003cem\u003eManhattan\u003c/em\u003e Bridge Overpass\u0026rdquo; (DUMBO). There we stumbled upon an \u003ca href=\"https://fadmarket.co/\"\u003eart fair\u003c/a\u003e, and took some pictures on the way to the Brooklyn bridge.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePhoto of Manhattan Bridge:\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20230805194109.jpg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePhoto of Brooklyn Bridge:\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20230805194139.jpg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eWe walked all the way to the beginning of the prominade on Tillary street to get onto the Brooklyn bridge. From where we started this resulted in a lot of unnecessary walking. If I was to do this again, I would\u0026rsquo;ve climbed the staircase located in the \u003ca href=\"https://www.brooklynbridgepark.org/places-to-see/brooklyn-bridge/\"\u003eunderpass of Washington street and Prospect street\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20230805205125.jpg\" alt=\"In the middle of the bridge\"\u003e\u003c/p\u003e\n\u003cp\u003eAfter crossing the bridge and reaching our 20k steps for the day, we found the nearest bench in city hall park and looked up places to eat. While doing so, we saw a biker run over the wings of a pigeon. At least for the pigeon, a little girl fed it Pringles afterwards.\u003c/p\u003e\n\u003cp\u003eWe ended up eating at a \u003ca href=\"http://hometownhotpot.com/\"\u003eHotPot\u003c/a\u003e place in the Manhattan Chinatown nearby. It\u0026rsquo;s harder to justify shared buffet style restaurants like unlimited hot pot or sushi when it\u0026rsquo;s just Clare and I. So how can we let go of that chance? The food turned out great and we\u0026rsquo;re looking out for the next excuse to eat it again.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20230805220511.jpg\" alt=\"Photo of Hot Pot meal with boiling soup and meats and veggies on the side\"\u003e\u003c/p\u003e\n",
    "date_published": "2023.09.19",
    "tags": ["Life"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/managing-mongodb-resource-usage-docker-compose/",
    "url": "https://brandonrozek.com/blog/managing-mongodb-resource-usage-docker-compose/",
    "title": "Limiting MongoDB Resource Usage within Docker Compose",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cem\u003eWarning: This technique does not work for the older Docker Compose v3\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eOne of my web services utilizes MongoDB as the database backend. It lives on one of the smaller sized virtual private servers with only 1GB of RAM and 1 CPU. Every so often I encounter a scenario where MongoDB is taking all of the sytem resources, so I started looking into how to limit it.\u003c/p\u003e\n\u003cp\u003eFirst, we can set some limits to container in general within docker-compose.\u003c/p\u003e\n\u003cp\u003eExample:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yml\" data-lang=\"yml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003emongo\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eimage\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003edocker.io/mongo:5.0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003emem_limit\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e256m\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003ecpus\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0.25\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis says that the mongo container can only use a maximum of 256MB of memory and can only use up to 25% of one CPU.\u003c/p\u003e\n\u003cp\u003eTo see a detailed list of possible options, check out the \u003ca href=\"https://docs.docker.com/config/containers/resource_constraints/\"\u003edocker documentation\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eKeep in mind that your memory and CPUs are not virtualized, therefore the container can see all the resources available, it just cannot request them.\u003c/p\u003e\n\u003cp\u003eThis may lead to some problems depending on the codebase. For example, in \u003ca href=\"https://www.mongodb.com/docs/manual/reference/configuration-options/#mongodb-setting-storage.wiredTiger.engineConfig.cacheSizeGB\"\u003eMongoDB\u003c/a\u003e the WiredTiger internal cache is by default set to 50% of the total amount of RAM minus 1GB.\u003c/p\u003e\n\u003cp\u003eLuckily we can change this default, by adding a flag within the docker-compose file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yml\" data-lang=\"yml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003emongo\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eimage\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003edocker.io/mongo:5.0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003emem_limit\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e256m\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003ecpus\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0.25\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003ecommand\u003c/span\u003e: --\u003cspan style=\"color:#ae81ff\"\u003ewiredTigerCacheSizeGB 0.25\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003ewiredTigerCacheSizeGB\u003c/code\u003e takes values between \u003ccode\u003e0.25\u003c/code\u003e GB and \u003ccode\u003e10000\u003c/code\u003e GB.\u003c/p\u003e\n",
    "date_published": "2023.09.19",
    "tags": ["Containers"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/how-to-safely-remove-dnf-repository/",
    "url": "https://brandonrozek.com/blog/how-to-safely-remove-dnf-repository/",
    "title": "How to Safely Remove a DNF Repository",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eAfter a major upgrade of Fedora or CentOS, we can be left with an outdated third-party vendor repository. Now maybe we went ahead and added the newer version of that repository, but how do we safely remove the old one?\u003c/p\u003e\n\u003cp\u003eFirst, we need to identify the repo id that we wish to remove.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ednf repolist\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eExample output:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003erepo id                          repo name\nfedora                           Fedora 38 - x86_64\nfedora-cisco-openh264            Fedora 38 openh264 (From Cisco) - x86_64\nfedora-modular                   Fedora Modular 38 - x86_64\nupdates                          Fedora 38 - x86_64 - Updates\nupdates-modular                  Fedora Modular 38 - x86_64 - Updates\nancient-repo                     Fedora 1\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe can remove all the packages from the repo id \u003ccode\u003eancient-repo\u003c/code\u003e with the following command:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ednf repository-packages ancient-repo remove\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUse this opportunity to double check that what it\u0026rsquo;s going to remove is okay for your system. Some applications may not have newer versions in the other repositories, so you might have to hunt those down separately.\u003c/p\u003e\n\u003cp\u003eAlso keep in mind, that this command will uninstall any unused dependencies from other repositories as well.\u003c/p\u003e\n\u003cp\u003eAfter removing the packages within the deprecated repository, we can remove it from the repo list.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erm /etc/yum.repos.d/ancient-repo.repo\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReplacing \u003ccode\u003eancient-repo\u003c/code\u003e with your repo id.\u003c/p\u003e\n",
    "date_published": "2023.09.19",
    "tags": ["Fedora"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/type-checking-javascript-files-using-typescript/",
    "url": "https://brandonrozek.com/blog/type-checking-javascript-files-using-typescript/",
    "title": "Type Checking Javascript Files Using Typescript",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThere\u0026rsquo;s a nice and easy way to type check Javascript files without having to set up an entire project or adjust the code. It only takes understanding some of the flags within the Typescript compiler.\u003c/p\u003e\n\u003cp\u003eTo install:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enpm install -g typescript\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTypecheck a file:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etsc --checkJs --allowJs example.js --noEmit\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eFlag\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--checkJs\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eAllow JavaScript files to be a part of your program.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--allowJs\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eEnable error reporting in type-checked JavaScript files\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--noEmit\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eDisable emitting files from a compilation.\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eSince \u003ccode\u003etsc\u003c/code\u003e is primarily a compile, it by default produces code output.\nWe\u0026rsquo;re only concerned with seeing any type errors, so we throw away\nthe compiled code with the flag \u003ccode\u003e--noEmit\u003c/code\u003e.\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003cp\u003eNow it\u0026rsquo;s likely that we\u0026rsquo;re using features of the standard library or the DOM in our Javascript code. We need to include a couple flags for Typescript to understand them.\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eFlag\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--target\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eSet the JavaScript language version for emitted JavaScript and include compatible library declarations.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--lib\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eSpecify a set of bundled library declaration files that describe the target runtime environment.\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eYou can see \u003ccode\u003etsc --help\u003c/code\u003efor the long list of options. Here\u0026rsquo;s an example for targetting \u003ccode\u003eES2022\u003c/code\u003e with the DOM library.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etsc --checkJs --allowJs --target es2022 --lib es2022,dom example.js --noEmit\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eIn an earlier version of this post, I had \u003ccode\u003e--outfile /dev/null\u003c/code\u003e instead of \u003ccode\u003e--noEmit\u003c/code\u003e.\n\u003ca href=\"https://veyndan.com/\"\u003eVeyndan\u003c/a\u003e kindly reached out and showed how the \u003ccode\u003e--noEmit\u003c/code\u003e flag is\nmore flexible in allowing for exported classes.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2023.09.19",
    "tags": ["JS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/representing-uncertainty-closed-world-assumption/",
    "url": "https://brandonrozek.com/blog/representing-uncertainty-closed-world-assumption/",
    "title": "Representing Uncertainty under the Closed World Assumption",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe \u003ca href=\"https://en.wikipedia.org/wiki/Closed-world_assumption\"\u003eclosed world assumption\u003c/a\u003e is used in many practical applications of logic (planning, prolog, databases) in order to lower the complexity class of the reasoning algorithms used. This assumes that any predicates that are stated are true and the ones that aren\u0026rsquo;t are false.\u003c/p\u003e\n\u003cp\u003eFor example, given the state: \u003ccode\u003e(color shirt1 red) (color shirt2 blue)\u003c/code\u003e, we know that \u003ccode\u003e(color shirt1 blue)\u003c/code\u003e is false solely because it wasn\u0026rsquo;t listed in the state.\u003c/p\u003e\n\u003cp\u003eThis means that for an individual predicate $P$, it can either represent true or false. To represent uncertainty, we need to extend to the use of two predicates instead of one.\u003c/p\u003e\n\u003cp\u003eThis approach is popular within conformant planning citing back to Palacios and Geffner in 2006. Consider our predicate $P$ for example. Instead of that predicate existing imagine we have the predicates. $K P$ and $K \\neg P$. Note that $K$ is not a higher-order predicate or operator but is instead part of the predicate name. Then our truth value for $P$ is as follows:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e$K P$\u003c/th\u003e\n          \u003cth\u003e$K \\neg P$\u003c/th\u003e\n          \u003cth\u003e$P$\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eTrue\u003c/td\u003e\n          \u003ctd\u003eFalse\u003c/td\u003e\n          \u003ctd\u003eTrue\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eFalse\u003c/td\u003e\n          \u003ctd\u003eTrue\u003c/td\u003e\n          \u003ctd\u003eFalse\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eFalse\u003c/td\u003e\n          \u003ctd\u003eFalse\u003c/td\u003e\n          \u003ctd\u003eUnknown\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eTrue\u003c/td\u003e\n          \u003ctd\u003eTrue\u003c/td\u003e\n          \u003ctd\u003eContradiction\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eWe\u0026rsquo;re still in the closed world assumption setting. The intuition behind this compilation is that the agent \u0026ldquo;knows what it knows and knows what it doesn\u0026rsquo;t know.\u0026rdquo; This approach doesn\u0026rsquo;t include any epistemic axions, but may be good enough to get started modeling within some of these systems.\u003c/p\u003e\n\u003cp\u003eThis is one way of encoding uncertainty in some predicate of $P$, but not the only way. An alternative is what I call the \u003cem\u003ediscovery encoding\u003c/em\u003e, mainly since I haven\u0026rsquo;t seen it in use in literature. If you have a citation with this encoding being used, please share with me.\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e$\\mathit{P\\_Value}$\u003c/th\u003e\n          \u003cth\u003e$P\\_Discovered$\u003c/th\u003e\n          \u003cth\u003e$P$\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eTrue\u003c/td\u003e\n          \u003ctd\u003eTrue\u003c/td\u003e\n          \u003ctd\u003eTrue\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eFalse\u003c/td\u003e\n          \u003ctd\u003eTrue\u003c/td\u003e\n          \u003ctd\u003eFalse\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eFalse\u003c/td\u003e\n          \u003ctd\u003eFalse\u003c/td\u003e\n          \u003ctd\u003eUnknown\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eTrue\u003c/td\u003e\n          \u003ctd\u003eFalse\u003c/td\u003e\n          \u003ctd\u003eContradiction\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eThis operates very similarly to \u003ca href=\"https://en.wikipedia.org/wiki/Option_type\"\u003eoptionals\u003c/a\u003e within functional programming. The idea is that one shouldn\u0026rsquo;t even consider the valuation of $P\\_Value$ until $P\\_Discovered$ is true. Since once $P\\_Discovered$ is true, then $P\\_Value = P$.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eReferences\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003ePalacios, Héctor, and Hector Geffner. \u0026ldquo;Compiling uncertainty away:  Solving conformant planning problems using a classical planner  (sometimes).\u0026rdquo; \u003cem\u003eAAAI\u003c/em\u003e. 2006.\u003c/p\u003e\n",
    "date_published": "2023.09.18",
    "tags": ["Logic","Automated Planning"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/obtaining-valid-ssl-certificates-internal-lan/",
    "url": "https://brandonrozek.com/blog/obtaining-valid-ssl-certificates-internal-lan/",
    "title": "Obtaining Valid SSL Certificates within an Internal LAN",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI previously wrote about a quick way to create a \u003ca href=\"https://brandonrozek.com/blog/internalca/\"\u003ecertificate authority for an internal LAN\u003c/a\u003e. This is for scenarios in which we don\u0026rsquo;t want that internal network to have access to the Internet or vice-versa. The main downside of this approach, is that for a computer to trust the SSL certificate created by a machine on that LAN, they need to have the public certificate loaded and trusted on that machine. This can become a pain to manage the larger the internal network grows.\u003c/p\u003e\n\u003cp\u003eHowever, if the internal network does have access to the Internet, we can use a different tool. Letsencrypt can issue a valid SSL certificate for your network without being able to directly access your network in question! It is able to do this though the \u003ccode\u003eDNS-01\u003c/code\u003e challenge.\u003c/p\u003e\n\u003cp\u003eThe way this work is that Letsencrypt asks your client to create a DNS \u003ccode\u003eTXT\u003c/code\u003e entry containing some special token. The client then either manually, manually with \u003ca href=\"https://certbot.eff.org/docs/using.html#hooks\"\u003ehooks\u003c/a\u003e, or via a \u003ca href=\"https://community.letsencrypt.org/t/dns-providers-who-easily-integrate-with-lets-encrypt-dns-validation/86438\"\u003eplugin\u003c/a\u003e edits the zone file on the DNS nameserver to add that entry. The Letsencrypt server then only needs to access that DNS nameserver in order to verify that you own the domain; Issuing your certificate upon success.\u003c/p\u003e\n\u003cp\u003eThis is an example on how to get a certificate for an example internal domain \u003ccode\u003einsiderexample.org\u003c/code\u003e using the Linode DNS provider.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecertbot certonly \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --dns-linode \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --dns-linode-credentials ~/.secrets/certbot/linode.ini \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  -d insiderexample.org\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2023.09.18",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/dns-01-challenge-wildcard-domain-letsencrypt/",
    "url": "https://brandonrozek.com/blog/dns-01-challenge-wildcard-domain-letsencrypt/",
    "title": "Using the DNS-01 Challenge to obtain a wildcard certificate on Letsecnrypt",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLetsencrypt lets anyone get a free SSL certificate in an easily automated way. It verifies that the user is allowed to issue a certificate for that domain by issuing a \u003cem\u003echallenge\u003c/em\u003e. Lets say that I want a certificate for \u003ccode\u003eexampledomain.com\u003c/code\u003e. The defaults for most clients is to use the \u003ccode\u003eHTTP-01\u003c/code\u003e challenge. This requires that we have a webserver installed on that domain running on port 80. As part of the challenge, the Letsencrypt client will drop a file within the appropriate webserver folder so that it gets served at \u003ccode\u003ehttp://exampledomain.com/.well-known/acme-challenge/\u0026lt;token\u0026gt;\u003c/code\u003e. For example on \u003ccode\u003enginx\u003c/code\u003e this could be at \u003ccode\u003e/var/www/exampledomain.com/.well-known/acme-challenge/\u0026lt;token\u0026gt;\u003c/code\u003e.  The Letsencrypt server will then verify that the file exists before issuing the certificate.\u003c/p\u003e\n\u003cp\u003eThis easily works for one domain, but what if we have many sub-domains we want added to the certificate? With the \u003ccode\u003eHTTP-01\u003c/code\u003e challenge, we need to test each sub-domain individually. Alternatively, we can use the \u003ccode\u003eDNS-01\u003c/code\u003e challenge to get issued a wildcard certificate. With one wildcard certificate (e.g \u003ccode\u003e*.exampledomain.com\u003c/code\u003e) we can secure \u003ccode\u003ea.exampledomain.com\u003c/code\u003e, \u003ccode\u003eb.exampledomain.com\u003c/code\u003e and many more!\u003c/p\u003e\n\u003cp\u003eLetsecnrypt verifies that the user is allowed to claim all these subdomains, by seeing if the user has access to the DNS zone file for that domain. The idea is that if the user is able to change the DNS anyways, then the user could\u0026rsquo;ve gone through the process of installing a webserver at that IP. With access to the DNS zone file, the user would have to create a \u003ccode\u003eTXT\u003c/code\u003e record.\u003c/p\u003e\n\u003cp\u003eNow this process could be done manually on Certbot by using the \u003ccode\u003e--manual\u003c/code\u003e flag. However, in the spirit of automation, there are many plugins existent to help streamline the process. This list of \u003ca href=\"https://community.letsencrypt.org/t/dns-providers-who-easily-integrate-with-lets-encrypt-dns-validation/86438\"\u003eDNS providers\u003c/a\u003e shows which provider supports this feature and in what clients.\u003c/p\u003e\n\u003cp\u003eIf your provider isn\u0026rsquo;t supported on this list, not all hope is lost. Under the manual plugin one can use the  \u003ca href=\"https://certbot.eff.org/docs/using.html#hooks\"\u003ehooks\u003c/a\u003e \u003ccode\u003e--manual-auth-hook\u003c/code\u003e and \u003ccode\u003e--manual-cleanup-hook\u003c/code\u003e to execute external scripts to access the DNS provider\u0026rsquo;s API.\u003c/p\u003e\n\u003cp\u003eThis wiki page for \u003ca href=\"https://eff-certbot.readthedocs.io/en/stable/using.html#dns-plugins\"\u003eCertbot\u003c/a\u003e shows the list of supported providers and how to structure the command line arguments. For example, I use \u003ccode\u003eNginx\u003c/code\u003e as my webserver and Linode as my DNS nameserver.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecertbot certonly \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --dns-linode \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --dns-linode-credentials ~/.secrets/certbot/linode.ini \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  -d *.exampledomain.com\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2023.09.18",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/progressive-enhancement-page-transitions-hugo-htmx/",
    "url": "https://brandonrozek.com/blog/progressive-enhancement-page-transitions-hugo-htmx/",
    "title": "Progressive Enhancement of Page Transitions in Hugo with htmx",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eProgressive Enhancement is a philosophy that places emphasis on making sure that a website\u0026rsquo;s content is available to everyone. Additional functionality, such as JavaScript interactivity, are viewed as a nicety. This idea became popular around the time period when many news websites would show a blank screen if JavaScript was disabled.\u003c/p\u003e\n\u003cp\u003eHowever without JavaScript, transitioning between pages can flash a white screen in between. This can be jarring for websites using a non-white background. Single page applications attempt to resolve this by requesting webpages via JavaScript and dynamically reshuffling the page.\u003c/p\u003e\n\u003cp\u003ePjax is a technique designed to marry these two ideas. \u003ca href=\"https://gander.wustl.edu/~wilson/store/yui/docs/pjax/index.html\"\u003eThe name Pjax stands for \u003ccode\u003epushState\u003c/code\u003e + \u003ccode\u003eAjax\u003c/code\u003e\u003c/a\u003e. The idea is that when a person clicks a link, instead of the browser loading an entirely new page, the local  JavaScript would issue an Ajax request. Once the request is fulfilled it would replace hte current screen contents with the request and update the browser\u0026rsquo;s history via a \u003ccode\u003epushState\u003c/code\u003e. Browsers that don\u0026rsquo;t support this feature will result in a normal full page load when the link is clicked.\u003c/p\u003e\n\u003cp\u003eOver the years several JavaScript libraries have popped up to fill this need. There was the classic \u003ca href=\"https://clarle.github.io/yui3/yui/docs/pjax/\"\u003e\u003ccode\u003epjax\u003c/code\u003e\u003c/a\u003e library which handled this. Then \u003ca href=\"https://intercoolerjs.org/\"\u003e\u003ccode\u003eintercooler.js\u003c/code\u003e\u003c/a\u003e came around to allow you to specify Ajax requests via HTML attributes. Finally, \u003ca href=\"https://htmx.org/\"\u003e\u003ccode\u003ehtmx\u003c/code\u003e\u003c/a\u003e extends these HTML attributes beyond Ajax and into CSS transitions and WebSockets.\u003c/p\u003e\n\u003cp\u003eThis post will show how you can use \u003ccode\u003ehtmx\u003c/code\u003e to easily create page transitions in a progressively enhanced way.  The quickest way to achieve this is with an attribute called \u003ccode\u003ehx-boost\u003c/code\u003e. This can be used by \u003ccode\u003e\u0026lt;a\u0026gt;\u003c/code\u003e tags in HTML for links and \u003ccode\u003e\u0026lt;form\u0026gt;\u003c/code\u003e tag for form submissions. By default, these two HTML elements will issue a full page load to a new URL once they are clicked. However with \u003ccode\u003ehx-boost\u003c/code\u003e, it will replace the full page load with a \u003ccode\u003eajax\u003c/code\u003e request and replace the \u003ccode\u003einnerHTML\u003c/code\u003e of the HTML node\u0026rsquo;s contents with the response.\u003c/p\u003e\n\u003cp\u003eAnother nice feature of \u003ccode\u003ehtmx\u003c/code\u003e is that we don\u0026rsquo;t need to add \u003ccode\u003ehx-boost\u003c/code\u003e for every HTML node. Instead, this attribute can be \u003cem\u003einherited\u003c/em\u003e. This allows us to put \u003ccode\u003ehx-boost=\u0026quot;true\u0026quot;\u003c/code\u003e on the body of the document and every form and link will be boosted. If we want to simulate a page transition, instead of replacing the inner HTML of the link or form, we can replace the entire inner HTML of the body of the document using \u003ccode\u003ehx-indicator\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003ebody\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehx-boost\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;true\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehx-indicator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;body\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ebody\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThough we have to do a little more work if we want a smooth page transition. HTMX uses classes to denote the \u003ca href=\"https://htmx.org/docs/#request-operations\"\u003elifecycle of the request\u003c/a\u003e. When an element sends off a request is, the \u003ccode\u003ehtmx-request\u003c/code\u003e class is added to whichever node is spcified in \u003ccode\u003ehx-indicator\u003c/code\u003e (itself by default).  We can use this to have a simple fade out transition in the beginning of the request.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003ehtmx-request\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eopacity\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003etransition\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eopacity\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e200\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003ems\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease-in\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOnce the AJAX request is satisfied, the \u003ccode\u003ehtmx-request\u003c/code\u003e class is replaced with \u003ccode\u003ehtmx-swapping\u003c/code\u003e to indicate that the content is about to be replaced.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003ehtmx-swapping\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eopacity\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ca href=\"https://htmx.org/attributes/hx-swap/\"\u003eUnless a swap delay is specified\u003c/a\u003e, the content is then quickly replaced which then puts the node in a settled state. This is when we should fade in to see the new content\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003ehtmx-settling\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eopacity\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003etransition\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eopacity\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e200\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003ems\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease-in\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUsing the htmx library, we can have nice page transitions with one line of HTML code, an optional 11 lines of CSS, and zero lines of custom JavaScrpt code. The small transitions are especially nice in low-bandwidth scenarios when a response to an ajax request isn\u0026rsquo;t immediately received. It gives some visual indication that clicking the link still did something.\u003c/p\u003e\n",
    "date_published": "2023.07.21",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/proactive-defederation/",
    "url": "https://brandonrozek.com/blog/proactive-defederation/",
    "title": "Proactive Defederation, is it worth it?",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eMeta recently launched Threads in an effort to siphon dissatisfied Twitter users. In this process, they are rumored to pursue ActivityPub support. This would allow users on Mastodon and other Fediverse platforms to communicate with people on Threads.\u003c/p\u003e\n\u003cp\u003eThe rumor has been circulating for a few months now, even when Threads was referred to its codename \u003cem\u003eProject 92\u003c/em\u003e. As such, many people have already voiced their opinion of this move. A group of moderators even banded together to make a \u003ca href=\"https://fedipact.online/\"\u003ewebsite declaring their opposition of Meta joining the Fediverse\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThe general argument is that Facebook is \u003cem\u003ebad\u003c/em\u003e and \u003ca href=\"https://seirdy.one/posts/2023/06/20/defederating-p92/#incompatible-values-and-complicity\"\u003ehas a bad track record\u003c/a\u003e. Thus, many people from the Mastodon community want to disassociate from Meta. The tool for disassociation on the Fediverse is called \u003cem\u003ede-federation\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eThe idea behind the Fediverse is that servers can \u003cem\u003efederate\u003c/em\u003e with each other. This means that servers can share data (whether its statuses or posts) with other servers as they talk a common language. Admins of each of these servers can select which platforms they want to \u003cem\u003eblock\u003c/em\u003e. Refusing to display content from blocked servers is called de-federation.\u003c/p\u003e\n\u003cp\u003eThis technique is commonly used to block content from servers that often promote illegal or offensive content. Though it can also be used in cases where servers do not align with the ethics of a community. Since Facebook \u0026ldquo;does not match the moral compass\u0026rdquo; of several Mastodon communities, they seek to de-federate proactively. Before any questionable content appears.\u003c/p\u003e\n\u003cp\u003eThe hope of these admins, is that if enough servers de-federate, it effectively nulls the benefits of adding ActivityPub support. Blocking the millions of users that signed up for Threads from interacting with other Mastodon users.\u003c/p\u003e\n\u003cp\u003eOf course, there will be some server admins that prioritize free speech and alternative values, but we\u0026rsquo;ll get into that later\u0026hellip;\u003c/p\u003e\n\u003cp\u003eInstead of going further into the ethics of the situation. I\u0026rsquo;m curious on the potential ramifications of Threads both sucessfully and unsucessfully federating with the majority of the Fediverse.\u003c/p\u003e\n\u003ch2 id=\"successful-federation\"\u003eSuccessful Federation\u003c/h2\u003e\n\u003cp\u003eLets say somehow Threads manages to federate with enough servers, such that the majority of current Mastodon users can interact with Threads users.\u003c/p\u003e\n\u003cp\u003eIn this world, Threads will be by far the largest user instance. This would not be too unexpected, since they have the engineers on staff to build infrastructure that can support millions of people. As a consequence, the majority of content would then be generated via Threads.\u003c/p\u003e\n\u003cp\u003eThis content will likely not follow the cultural norms of the existing Mastodon community. The majority of these cultural norms are built with an accessibility first mindset. Examples of this include: adding captions to photos, title-casing hashtags, adding content warnings to inappropriate or sensitive topics.\u003c/p\u003e\n\u003cp\u003eIn a world where Meta is the largest Fediverse player, they will likely try to push the ActivityPub standards to whatever their priority of the time is. There\u0026rsquo;s a few ways this can play out, they can try to be the majority representation of the board updating the ActivityPub standard. They could also just implement features themselves and bully others to adopt and support the extensions to the standard they craft.\u003c/p\u003e\n\u003cp\u003eWhat features do I imagine Meta will want to add? I would imagine any that pertain to advertising and monetization, but we can leave that to your imagination.\u003c/p\u003e\n\u003ch2 id=\"failed-federation\"\u003eFailed Federation\u003c/h2\u003e\n\u003cp\u003eIn this world, enough large instances de-federate from Threads, that most current users of Mastodon wouldn\u0026rsquo;t see content from them.\u003c/p\u003e\n\u003cp\u003eThe ideal in this case is that some sort of status quo is maintained and users of these differing communities do not need to interact with each other.\u003c/p\u003e\n\u003cp\u003eThough I\u0026rsquo;m not sure if that\u0026rsquo;s the case. There might be some internal resistance from Mastodon users that want to use the Fediverse to keep in touch with their friends and family. Sadly the majority of non-techie friends and family will likely be using Threads or another platform from a major player. This added pressure can either cause the de-federated servers to federate again. Alternatively, it can cause some of these users to migrate over to free speech-instances or create another account on these or on Threads itself.\u003c/p\u003e\n\u003cp\u003eUnfortunately for many free-speech instances, they often harbor offensive speech. The question then is: does Threads federate with these servers and then have to take many active measures to make sure the most extreme content doesn\u0026rsquo;t surface to its users?\u003c/p\u003e\n\u003cp\u003eOr does it just block the free-speech instances entirely? If Threads de-federates from the free speech instances, then there wouldn\u0026rsquo;t be enough other instances to federate with making this a failed ActivityPub  attempt.\u003c/p\u003e\n\u003cp\u003eThere could be an attempt to fork the ActivityPub standard, but without some buy-in from the existing community, this is likely to fail.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eHistorically, this reminds me of when Google Talk/Hangouts supported XMPP. At the time I used Pidgin with the OTR plugin to communicate with friends and family. Sadly they henceforth abandoned XMPP support and I don\u0026rsquo;t use much of that or Hangouts anymore.\u003c/p\u003e\n\u003cp\u003eWill we see a similar approach from Meta to extend, embrace, and extinguish the ActivityPub protocol?\u003c/p\u003e\n",
    "date_published": "2023.07.10",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/adventures-in-bird-watching/",
    "url": "https://brandonrozek.com/blog/adventures-in-bird-watching/",
    "title": "Adventures in Bird Watching",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eRecently for the first time I embarked on a bird watching tour. Part of the event listing contained the following:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eDon\u0026rsquo;t forget to bring a pair of binoculars!\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eNow I can buy the first pair of binoculars I find in a supermarket. Though, I always enjoy over-analyzing purchasing decisions and this isn\u0026rsquo;t an exception.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWhat considerations do you need to make when buying binoculars?\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eMost pairs of binoculars report a NxM measurement.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eN here denotes the magnification factor\u003c/li\u003e\n\u003cli\u003eM denotes the diameter of the lens in millimeters\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThis isn\u0026rsquo;t a case where you buy the highest value in each section and call it a day. There are trade-offs:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eMagnification Factor\n\u003cul\u003e\n\u003cli\u003eHigher means you can see farther away.\u003c/li\u003e\n\u003cli\u003eLower means you have a greater field of view.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eDiameter Length\n\u003cul\u003e\n\u003cli\u003eHigher means that more light gets captured which makes the overall image brighter.\u003c/li\u003e\n\u003cli\u003eLower means that the binocular weighs less which can be important for long hikes.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eFor a typical bird watching session, you normally don\u0026rsquo;t look at birds that are too far away. In this case, you want to prioritize a greater field of view which can make the viewing feel more stable. The commonly recommended magnification factor is 8.\u003c/p\u003e\n\u003cp\u003eBinocular weight is generally only a massive concern for people hiking long distances. If the bird watching session is a quick one, then you should prioritize having a brighter picture than a lighter binocular. The commonly recommended diameter length is 42mm.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eTherefore, the commonly recommended specification for bird-watching binoculars is 8x42.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eOnce you\u0026rsquo;re undergoing the bird watching session, how do you know what bird you\u0026rsquo;re looking at? In the ideal case, you\u0026rsquo;re already an expert and can identify it once you see it. Second best case, you have a guide that can tell you what it is. For the majority of us, however, we will have neither. In this case, we can rely on technology.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ca href=\"https://merlin.allaboutbirds.org/\"\u003eMerlin\u003c/a\u003e is a mobile app that can help identify birds.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eDeveloped by the Cornel lab of Orthnology, Merlin can provide a database of bird photos and sounds, as well as tools to help identify them.\u003c/p\u003e\n\u003cp\u003eIf you remember before a session, you can download localized bird data. This way during the session you can look at photos and sounds of birds that are typically found nearby your location.\u003c/p\u003e\n\u003cp\u003eNot only do they provide a database, but the app also allows for identification by both visuals and sound. When you ask the app the identify a bird visually, it\u0026rsquo;ll ask for your location, size of bird, colors found on the bird, and its behavior.\u003c/p\u003e\n\u003cp\u003eBirds often have fairly distinct calls. Merlin can also help identify based on these sounds.\u003c/p\u003e\n\u003cp\u003eSo how did my bird watching tour go? Our group spotted the following birds in New York:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eTurkey Vulture\u003c/li\u003e\n\u003cli\u003eCooper’s Hawk\u003c/li\u003e\n\u003cli\u003eRed-Shouldered Hawk\u003c/li\u003e\n\u003cli\u003eRed-tailed Hawk\u003c/li\u003e\n\u003cli\u003eRed-eyed Vireo\u003c/li\u003e\n\u003cli\u003eBlue Jay\u003c/li\u003e\n\u003cli\u003eTree Swallow\u003c/li\u003e\n\u003cli\u003eBarn Swallow\u003c/li\u003e\n\u003cli\u003eGray Catbird\u003c/li\u003e\n\u003cli\u003eWood thrush\u003c/li\u003e\n\u003cli\u003eCedar Waxwing\u003c/li\u003e\n\u003cli\u003eAmerican Goldfinch\u003c/li\u003e\n\u003cli\u003eSong sparrow\u003c/li\u003e\n\u003cli\u003eRed-winged blackbird\u003c/li\u003e\n\u003cli\u003eCommon yellowthroat\u003c/li\u003e\n\u003cli\u003eYellow Warbler\u003c/li\u003e\n\u003c/ul\u003e\n",
    "date_published": "2023.07.08",
    "tags": ["Life"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/photograph-ordinary-times/",
    "url": "https://brandonrozek.com/blog/photograph-ordinary-times/",
    "title": "Photograph the Ordinary Times",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhenever I go out I enjoy taking photos. Given how easy it is to take photos nowadays, I aim to capture the moment through more candid expressions. Not all of the photos turn out nice or flattering, but there are usually at least a few that help bring me back to that time.\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s easy to remember to take photos when a special event is going on. Perhaps you\u0026rsquo;re visiting a location for the first time, or you are hanging out with people. What\u0026rsquo;s harder to remember is taking photos in locations you constantly go to or around the house.\u003c/p\u003e\n\u003cp\u003eWhat is interesting about ordinary times like these? They are a dime a dozen. If I photograph my simple moments everyday, I\u0026rsquo;ll clutter my photo library.\u003c/p\u003e\n\u003cp\u003eThat may be true, but I argue that they at least deserve a spot in your photo library.\u003c/p\u003e\n\u003cp\u003eI find that my life constantly changes. It may be because I\u0026rsquo;m still young and moving around places. But I believe this is still the case even if you settle down in a location.\u003c/p\u003e\n\u003cp\u003eFor example, I used to go to Spillin\u0026rsquo; the Beans in Troy, NY for breakfast every week. Now they closed down. Since I have taken a couple photos of that location, I can look back and remember my times there.\u003c/p\u003e\n\u003cp\u003ePeople come and go in our lives. Friends may move to different locations or kids move out of the house. What we might at one point taken for granted and call ordinary, may be gone the next moment.\u003c/p\u003e\n\u003cp\u003eWe ourselves undergo change over time. Both through age and sometimes in the way we style ourselves.\u003c/p\u003e\n\u003cp\u003eTherefore, next time you go out and visit a coffee shop or park that you frequent. Snap a photo. Future you will thank you later for the memory.\u003c/p\u003e\n",
    "date_published": "2023.06.17",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/rebalancing-portfolio-buy-low-sell-high/",
    "url": "https://brandonrozek.com/blog/rebalancing-portfolio-buy-low-sell-high/",
    "title": "Rebalancing Portfolios: The buy low sell high of investing",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn uncertain financial times, investments can take a temporary hit in value. A non-prudent investor may have their emotions get the better of them and start selling their positions out of fear of a further permanent drop in value. One of the core assumptions in investing is that general market valuations increase over time. Therefore, assuming that your positions are well diversified, you should not assume that valuations are \u003cem\u003epermanently\u003c/em\u003e lowered.\u003c/p\u003e\n\u003cp\u003eHow do we know when to buy and sell positions then? One primary goal is to avoiding \u003cem\u003ebuying high and selling low\u003c/em\u003e. There\u0026rsquo;s a simple procedure we can follow that guarantees the opposite. That procedure is called \u003cem\u003erebalancing\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eThe process of rebalancing assumes that you have some fixed goal distribution of your assets. For example, let\u0026rsquo;s say you want your portfolio to consist of 80% stocks and 20% bonds. Over time as valuations fluctuate, your portfolio will fall out of balance. For example, it can be 87% stocks and 13% bonds.\u003c/p\u003e\n\u003cp\u003eThere could be a couple reasons this happened. The valuation of the stock positions could\u0026rsquo;ve risen and/or the valuation of the bond positions could\u0026rsquo;ve fallen. To get the portfolio back to the 80/20 split, we can sell off some of the stock positions and/or buy some more of the bond positions. Given the circumstances that we\u0026rsquo;re rebalancing in, it means we\u0026rsquo;re likely selling the stock positions at its high and/or buying bond positions at its low.\u003c/p\u003e\n\u003cp\u003eSo when should one rebalance? There\u0026rsquo;s not a hard-fast rule and it\u0026rsquo;s dependent on taxation and trading fees, but generally people either follow a schedule (1-4 times a year) or rebalance when their distribution strays by more than say 5%.\u003c/p\u003e\n",
    "date_published": "2023.06.17",
    "tags": ["Finance"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/diversified-investments-index-fund/",
    "url": "https://brandonrozek.com/blog/diversified-investments-index-fund/",
    "title": "Diversified Investing using Index Funds",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen it comes to investments, there\u0026rsquo;s a common saying:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eDiversification is the only free lunch in investing\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eTo diversify with individual securities, one would need to buy equities and bonds from many organizations of differing sectors and capital size. For most people this can turn out to be prohibitively expensive.\u003c/p\u003e\n\u003cp\u003eMutual funds and ETFs address this problem by pooling together money from multiple parties and purchasing securities based off some stated objective. Depending on the holdings, the fund may be considered well diversified. Some funds don\u0026rsquo;t require minimum investments which makes it easier for people to get started.\u003c/p\u003e\n\u003cp\u003eNot all funds are created equal however. Before investing in any fund, you should be informed and take a look at their prospectus to get an idea of their objectives and current assets before investing.\u003c/p\u003e\n\u003cp\u003eOne way to characterize a fund is if it has an active or passive investment style. The goal of an active fund is to beat overall market trends. This means that the fund manager is often buying and selling securities to take advantage of price fluctuations. Though since it\u0026rsquo;s neigh impossible to predict market conditions, it is common for active funds to not beat the overall market.\u003c/p\u003e\n\u003cp\u003ePassive funds take a more buy-and-hold type of mentality. The core assumption is that market valuations increase over time. Therefore if a fund holds a given security over a long period of time, then is likely to result in some positive return.\u003c/p\u003e\n\u003cp\u003eThough what specific securities do passive funds buy and hold? One common category of passive funds is indexing. This is where the fund holds securities in the same weighted proportion as reported by some index. For example the most commonly known index, the S\u0026amp;P 500, consists of 500 of the largest stocks in the United States weighted by their market value.\u003c/p\u003e\n\u003cp\u003eFor large-cap indexes (those that contains large companies), there is little turnover in the companies within the index. This makes it perfect for passively managed funds. The S\u0026amp;P 500 has a historical turnover rate of 4.4%.\u003c/p\u003e\n\u003cp\u003eRecall the saying about diversification being a free lunch. What would be considered one of the most diversified funds? If we\u0026rsquo;re looking at solely the US, we can consider the Dow Jones US Total Stock Market Index. This contains a market cap weighting of 4193 companies at this time of writing. That\u0026rsquo;s 8 times larger than the S\u0026amp;P 500.\u003c/p\u003e\n\u003cp\u003eTherefore, given the assumption that equities on average increase in value over time. A well diversified passive fund can be a great investment. These funds generally have lower expense ratios (fees) as not as much human attention is needed to maintain the portfolio.\u003c/p\u003e\n\u003cp\u003eA risk that\u0026rsquo;s unique to some index funds is tracking error. A fund may not have enough money to purchase all securities at the reported proportions. Therefore, some will perform statistical techniques to approximate the index. Also since index funds are bound by the index they\u0026rsquo;re following, the performance will be correlated and the fund may not take advantage of price fluctuations or attempt to lower drawdowns.\u003c/p\u003e\n\u003cp\u003eDisclaimer: I am not an investment analyst and this does not constitute investment advice. This information is what I\u0026rsquo;ve learned recently and I encourage you to read up on these concepts and look at the prospectus before investing.\u003c/p\u003e\n",
    "date_published": "2023.06.16",
    "tags": ["Finance"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/functional-completeness/",
    "url": "https://brandonrozek.com/blog/functional-completeness/",
    "title": "Functional Completeness",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eYou might have heard somewhere that AND as well as NOT are the only two operators you need in propositional logic in order to represent all possible truth tables.\u003c/p\u003e\n\u003cp\u003eThis is often used to simplify circuit design. Instead of having a custom chip for every other operator, it\u0026rsquo;s generally more cost effective to only have two distinct chips but use more of them. But what if we want to find other combinations?\u003c/p\u003e\n\u003cp\u003eIn formal terms, we\u0026rsquo;re looking for other \u003ca href=\"https://en.wikipedia.org/wiki/Functional_completeness\"\u003efunctional complete\u003c/a\u003e sets.\u003c/p\u003e\n\u003cp\u003eIn this post, we\u0026rsquo;ll provide an overview for finding these sets. Finding some examples of functional complete sets along the way.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eThe intuition here is to know that our set $\\mathcal{S}$ is functionally complete if we can replicate the other operators using the ones only within $\\mathcal{S}$. Let\u0026rsquo;s refresh ourselves in what the complete set of operators are:\n$$\n\\{\\mathtt{AND}, \\mathtt{OR}, \\mathtt{NOT}, \\mathtt{IF}, \\mathtt{IFF}\\}\n$$\nThe first step would normally be to remove one operator from the above set and see if you can derive it using the other operators. For brevity of this post though, we\u0026rsquo;ll skip sets of length four and look directly at sets of length three.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eExample 1:\u003c/strong\u003e Show that $\\mathcal{S} = \\{\\mathtt{AND}, \\mathtt{OR}, \\mathtt{NOT}\\}$ is functionally complete.\u003c/p\u003e\n\u003cp\u003eIn this example, we\u0026rsquo;re missing both $\\mathtt{IF}$ and $\\mathtt{IFF}$. Using the operators of $\\mathcal{S}$ we can define the missing operators to be (in prefix notation):\n$$\n\\begin{align*}\n(\\mathtt{IF}\u003cdel\u003eA\u003c/del\u003eB) \u0026amp;\\equiv (\\mathtt{OR}~(\\mathtt{NOT}\u003cdel\u003eA)\u003cdel\u003eB) \\\\\n(\\mathtt{IFF}\u003cdel\u003eA\u003c/del\u003eB) \u0026amp;\\equiv (\\mathtt{AND}\u003c/del\u003e(\\mathtt{OR}\u003cdel\u003eA\u003c/del\u003eB) (\\mathtt{OR}\u003c/del\u003e(\\mathtt{NOT}\u003cdel\u003eA)\u003c/del\u003e (\\mathtt{NOT}~B)))\n\\end{align*}\n$$\nFor now, it\u0026rsquo;ll be beyond the scope on how to find the correct combination of operators within $\\mathcal{S}$.\u003c/p\u003e\n\u003cp\u003eSince we\u0026rsquo;re able to represent the missing operators in $\\mathcal{S}$ the set is functionally complete.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eExample 2:\u003c/strong\u003e Show that $\\mathcal{S} = \\{\\mathtt{NOT},  \\mathtt{AND}, \\mathtt{IF}\\}$ is functionally complete.\u003c/p\u003e\n\u003cp\u003eWe have to show that we can represent the missing operators $\\mathtt{OR}$ and $\\mathtt{IFF}$ using only elements of $\\mathcal{S}$.\n$$\n\\begin{align*}\n(\\mathtt{IFF}\u003cdel\u003eA\u003c/del\u003eB) \u0026amp;\\equiv (\\mathtt{AND}~ (\\mathtt{IF}~ A~ B)~ (\\mathtt{IF}~ B~ A)) \\\\\n(\\mathtt{OR}~ A~ B) \u0026amp;\\equiv (\\mathtt{NOT}~ (\\mathtt{AND}~ (\\mathtt{NOT}~ A)~ (\\mathtt{NOT}~ B)))\n\\end{align*}\n$$\nThis shows that the set $\\mathcal{S}$ is functionally complete.\u003c/p\u003e\n\u003cp\u003eAt this point we know that $\\{\\mathtt{AND}, \\mathtt{OR}, \\mathtt{NOT}\\}$ and $\\{\\mathtt{NOT},  \\mathtt{AND}, \\mathtt{IF}\\}$ are functionally complete. Now comes the exciting part, finding functionally complete sets with only two operators.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eWhat knowledge can we derive from $\\{\\mathtt{AND}, \\mathtt{OR}, \\mathtt{NOT}\\}$?\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eLet\u0026rsquo;s consider all possible sets of length two:\n$$\n\\{\\mathtt{AND}, \\mathtt{NOT}\\} \\\\\n\\{\\mathtt{AND}, \\mathtt{OR}\\} \\\\\n\\{\\mathtt{OR}, \\mathtt{NOT}\\}\n$$\n\u003cstrong\u003eExample 3:\u003c/strong\u003e Is ${\\mathtt{AND}, \\mathtt{NOT}}$ functionally complete?\u003c/p\u003e\n\u003cp\u003eWe need to show that we can derive $\\mathtt{OR}$.\n$$\n(\\mathtt{OR}~ A~ B) \\equiv (\\texttt{NOT}~ (\\texttt{AND}~ (\\texttt{NOT}~ A)~ (\\texttt{NOT}~ B)))\n$$\nThis means using only $\\mathtt{AND}$ and $\\mathtt{NOT}$ we can represent $\\{\\mathtt{AND}, \\mathtt{OR}, \\mathtt{NOT}\\}$ which is functionally complete. Therefore, $\\mathcal{S} = \\{ \\mathtt{AND}, \\mathtt{NOT}\\}$ is functionally complete.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eExample 4:\u003c/strong\u003e Is $\\{\\mathtt{AND}, \\mathtt{OR}\\}$ functionally complete?\u003c/p\u003e\n\u003cp\u003eNo, because we cannot represent $\\mathtt{NOT}$ using both $\\mathtt{AND}$ and/or $\\mathtt{OR}$. In a future iteration of this point I might describe why this is the case.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eExample 5:\u003c/strong\u003e Is $\\mathcal{S} = \\{\\mathtt{OR}, \\mathtt{NOT}\\}$ functionally complete?\u003c/p\u003e\n\u003cp\u003eWe need to show that we can derive $\\mathtt{AND}$.\n$$\n(\\mathtt{AND}~ A~ B) \\equiv (\\texttt{NOT}~ (\\texttt{OR}~ (\\texttt{NOT}~ A)~ (\\texttt{NOT} ~B))\n$$\nTherefore, $\\mathcal{S}$ is functionally complete.\u003c/p\u003e\n\u003cp\u003eSimilarly, we can look at $\\{\\mathtt{NOT}, \\mathtt{AND}, \\mathtt{IF}\\}$ and consider pairs of operators that might still be functionally complete.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eExample 6:\u003c/strong\u003e Is $\\{\\mathtt{AND}, \\mathtt{IF}\\}$ functionally complete?\u003c/p\u003e\n\u003cp\u003eNo, because you cannot represent $\\mathtt{NOT}$.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eExample 7:\u003c/strong\u003e Is $\\{\\mathtt{NOT}, \\mathtt{IF}\\}$ functionally complete?\u003c/p\u003e\n\u003cp\u003eHere we\u0026rsquo;re missing $\\mathtt{AND}$, so if we can derive it using the two operators, then the set is functionally complete.\n$$\n(\\mathtt{AND}~ A~ B) \\equiv (\\mathtt{NOT}~ (\\mathtt{IF}~ A~ (\\mathtt{NOT}~ B)))\n$$\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eThere are no single operators that are functionally complete in this example. Though the above gives somewhat of a procedure for finding all the sets of functionally complete operators. Here\u0026rsquo;s a summary:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eStart with your base set of operators $\\mathcal{S} = {x_1, x_2, \\dots, x_n}$\u003c/li\u003e\n\u003cli\u003eRemove $x \\in \\mathcal{S}$ such that $\\mathcal{S}^\\prime = \\mathcal{S} - {x}$\u003c/li\u003e\n\u003cli\u003eDerive $x$ using the other operators in $\\mathcal{S}^\\prime$.\u003c/li\u003e\n\u003cli\u003eIf possible, add $\\mathcal{S}^\\prime$ to $\\mathcal{S}_{n - 1}$ the set of functionally complete operators of length $n - 1$\u003c/li\u003e\n\u003cli\u003eRepeat steps (2) and (3) on each $\\mathcal{S}^\\prime \\in \\mathcal{S}_{n - 1}$ to create $\\mathcal{S}_{n - 2}$\u003c/li\u003e\n\u003cli\u003eKeep going until no new functionally complete sets are found.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eI know that this is a high level article and that there\u0026rsquo;s some details that are left out, but please feel free to reach out if you have any questions.\u003c/p\u003e\n",
    "date_published": "2023.05.30",
    "tags": ["Logic"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/ai-fearmongering-regulatory-moat/",
    "url": "https://brandonrozek.com/blog/ai-fearmongering-regulatory-moat/",
    "title": "AI fearmongering for regulatory moats",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOn May 16th 2023, Sam Altman \u003ca href=\"https://www.washingtonpost.com/technology/2023/05/16/ai-congressional-hearing-chatgpt-sam-altman/\"\u003etestified before a congress subcommittee\u003c/a\u003e on the need for AI regulation. During so, he proposes three actions:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eForm a new government agency charged with licensing large AI models, and allow it to revoke it for companies whose don’t comply.\u003c/li\u003e\n\u003cli\u003eCreate a set of safety standards for AI models.\u003c/li\u003e\n\u003cli\u003eRequire independent audits, by independent experts.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eOkay I can get behind the idea behind \u0026ldquo;safety standards\u0026rdquo; but what about this new government agency? Why would Sam Altman the CEO of OpenAI want the creation of a new government agency?\u003c/p\u003e\n\u003cp\u003eSome (including me) speculate that he\u0026rsquo;s trying to create a \u003cem\u003eregulatory moat\u003c/em\u003e for OpenAI.\u003c/p\u003e\n\u003cp\u003eWe can see where this goes. There are thousands of AI startups trying to sell models of various kinds, the idea is that they all need to register and be regularly monitored by a government agency?\u003c/p\u003e\n\u003cp\u003eThis would probably lead to long wait times for these AI licenses with a few companies holding the majority of the licenses. Startups funded through Y-combinator rarely go beyond \u003ca href=\"https://ycuniverse.com/longevity-of-yc-startups-from-funding-through-acquisition/\"\u003etwo years\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s a great way of trying to stamp out competition really. Currently it\u0026rsquo;s highly expensive to train one of these large language models (LLMs) but would that always be the case? In the inevitable future where other companies have the compute to generate one of these models, OpenAI needs to find a way to keep its market share.\u003c/p\u003e\n\u003cp\u003eNow you might think I\u0026rsquo;m being to pessimistic. What if Sam Altman is trying to think about the best for society? If that\u0026rsquo;s the case, then I don\u0026rsquo;t think he would threaten to not \u003ca href=\"https://www.reuters.com/technology/openai-may-leave-eu-if-regulations-bite-ceo-2023-05-24/\"\u003eprovide AI services in the EU\u003c/a\u003e due to their stringent regulations.\u003c/p\u003e\n\u003cp\u003eHe\u0026rsquo;s walked back the comment since then, but let\u0026rsquo;s take a brief look at what the EU regulations look like.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eMost current version I\u0026rsquo;m aware of: \u003ca href=\"https://digital-strategy.ec.europa.eu/en/policies/regulatory-framework-ai%5D\"\u003ehttps://digital-strategy.ec.europa.eu/en/policies/regulatory-framework-ai]\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eSummary in Presentation Form: \u003ca href=\"https://www.ceps.eu/wp-content/uploads/2021/04/AI-Presentation-CEPS-Webinar-L.-Sioli-23.4.21.pdf\"\u003ehttps://www.ceps.eu/wp-content/uploads/2021/04/AI-Presentation-CEPS-Webinar-L.-Sioli-23.4.21.pdf\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eGenerative models are considered at the time of writing to be a \u0026ldquo;high-risk\u0026rdquo; AI model. The additional regulations the EU imposes on these companies are:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cul\u003e\n\u003cli\u003eadequate risk assessment and mitigation systems;\u003c/li\u003e\n\u003cli\u003ehigh quality of the datasets feeding the system to minimise risks and discriminatory outcomes;\u003c/li\u003e\n\u003cli\u003elogging of activity to ensure traceability of results;\u003c/li\u003e\n\u003cli\u003edetailed documentation providing all information necessary on the system and its purpose for authorities to assess its compliance;\u003c/li\u003e\n\u003cli\u003eclear and adequate information to the user;\u003c/li\u003e\n\u003cli\u003eappropriate human oversight measures to minimise risk;\u003c/li\u003e\n\u003cli\u003ehigh level of robustness, security and accuracy.\u003c/li\u003e\n\u003c/ul\u003e\u003c/blockquote\u003e\n\u003cp\u003eI feel like most of those bullet points fall under Altman\u0026rsquo;s \u0026ldquo;safety standards for AI models\u0026rdquo;. Though I think what he really has an issue with is the data privacy requirements set forth by the EU. In fact this is an often criticized part of these models trained on Internet data:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003ePersonally identifiable information\u003c/li\u003e\n\u003cli\u003eCopyright information\u003c/li\u003e\n\u003cli\u003eConspiracy/Misinformation\u003c/li\u003e\n\u003cli\u003eetc.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eMy personal opinion is that these models should not be trained on the above data, but this isn\u0026rsquo;t a new discussion. More recently, the issue of copyright has been brought up with generative code products such as GitHub Copilot. I am interested to see how it makes its way through the US court systems here.\u003c/p\u003e\n\u003cp\u003eAnother question I think we need to answer as a society is whether to hold companies accountable for defamation. There are already instances where \u003ca href=\"https://www.washingtonpost.com/technology/2023/04/05/chatgpt-lies/\"\u003eChatGPT incorrectly accused a law professor of sexual harrasment\u003c/a\u003e. With the push to \u003ca href=\"https://www.nytimes.com/2022/12/21/technology/ai-chatgpt-google-search.html\"\u003ereplace search\u003c/a\u003e with these automatically generated responses, we can see how misinformation may be propagated and then further reinforced by these models.\u003c/p\u003e\n\u003cp\u003eI don\u0026rsquo;t want to say that it\u0026rsquo;s a bad idea for congress to regulate AI. I\u0026rsquo;m mostly suggesting that perhaps we shouldn\u0026rsquo;t call upon people who hold direct financial interest to come up with the regulations that will apply to themselves. That might lead these actors to bring attention to certain issue while avoiding others\u0026hellip;\u003c/p\u003e\n",
    "date_published": "2023.05.28",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/popular-automated-website-attacks/",
    "url": "https://brandonrozek.com/blog/popular-automated-website-attacks/",
    "title": "Top 7 Attacks to My Website",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eRunning a public server on the Internet means that it\u0026rsquo;s bound to get attacked by automated scripts. Since I \u003ca href=\"https://brandonrozek.com/blog/goaccess/\"\u003erun analytics on my website\u003c/a\u003e, I\u0026rsquo;m able to see 404s. In other words, this list constitutes the top requests to my website that fail.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003ccode\u003e/wp-login.php\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eThis is the login page of a Wordpress website. Since this blog engine powers 43% of the Internet, it\u0026rsquo;s not surprising that this is a common target. Sadly for the bots, I don\u0026rsquo;t run this website using Wordpress.\u003c/p\u003e\n\u003col start=\"2\"\u003e\n\u003cli\u003e\u003ccode\u003exmlrpc.php\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eThis one I haven\u0026rsquo;t heard of before until looking it up. I know XML is a data format, and RPC means remote procedure call, but what is the attacker trying to exploit? \u003ca href=\"https://codex.wordpress.org/XML-RPC_Support\"\u003eAgain it\u0026rsquo;s Wordpress\u003c/a\u003e. It seems that this is some API gateway that Wordpress provides to connect with mobile devices, provide pingbacks, and others.\u003c/p\u003e\n\u003col start=\"3\"\u003e\n\u003cli\u003e\u003ccode\u003e/api/v1/instance\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eThrough the power of search this seems to be a \u003ca href=\"https://docs.joinmastodon.org/methods/instance/#v1\"\u003eAPI call to Mastodon\u003c/a\u003e! This specifically grabs generic information about the instance such as the number of users, number of statuses, restrictions, etc. I\u0026rsquo;ve considered at some point running a Mastodon instance, but maybe it\u0026rsquo;s better to leave it to the pros :)\u003c/p\u003e\n\u003col start=\"4\"\u003e\n\u003cli\u003e\u003ccode\u003e/.env\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eIt seems that the Javascript community likes using a \u003ccode\u003e.env\u003c/code\u003e file to keep environmental variables that hold the secrets of your application. Yikes! Make sure that you\u0026rsquo;re blocking this if you have it!\u003c/p\u003e\n\u003col start=\"5\"\u003e\n\u003cli\u003e\u003ccode\u003e/inbox\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eGiven #3, I feel like this is ActivityPub related. Though looking at how the \u003ca href=\"https://www.w3.org/TR/activitypub/\"\u003eactors are usually structured\u003c/a\u003e it\u0026rsquo;s generally \u003ccode\u003e/username/inbox\u003c/code\u003e. Maybe it\u0026rsquo;s related to email servers instead? I\u0026rsquo;m unsure.\u003c/p\u003e\n\u003col start=\"6\"\u003e\n\u003cli\u003e\u003ccode\u003e/status.php\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eI\u0026rsquo;m not sure what this is. Doesn\u0026rsquo;t seem to be Wordpress related. Maybe the attacker is hoping to get the output of \u003ccode\u003ephpinfo()\u003c/code\u003e in those \u0026ldquo;getting started with PHP\u0026rdquo; tutorials?\u003c/p\u003e\n\u003cp\u003eJames Oswald suggests that perhaps it\u0026rsquo;s the \u003ca href=\"https://github.com/pfsense/pfsense/blob/master/src/usr/local/www/status.php\"\u003efirewall status page in pfsense\u003c/a\u003e.\u003c/p\u003e\n\u003col start=\"7\"\u003e\n\u003cli\u003e\u003ccode\u003e/.git/config\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eI can see a situation where someone has a git repository of their website on the server itself and they push to it. Personally, I rsync the generated HTML files. Generally the config will contain the URLs of remote repositories and other settings. Not entirely sure what\u0026rsquo;s sensitive, but maybe someone can let me know.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003eThere you have it! The top automated attacks made to my website. If you have any additional information on any of these URL patterns please get in touch. I am curious what these bots are trying to do with the response of each of these queries.\u003c/p\u003e\n",
    "date_published": "2023.05.17",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/website-analytics-usefulness/",
    "url": "https://brandonrozek.com/blog/website-analytics-usefulness/",
    "title": "The Usefulness of Website Analytics for Personal Websites",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eShould personal websites contain analytics? One argument is that analytics create a perverse incentive for the author. Let\u0026rsquo;s say that someone spent a lot of time uploading their favorite \u003ca href=\"https://brandonrozek.com/menu/\"\u003erecipes\u003c/a\u003e which have gone mostly unnoticed. Meanwhile, their post on \u003ca href=\"https://brandonrozek.com/blog/wildcarddomainspihole/\"\u003ewildcard domains in PiHole\u003c/a\u003e has thousands of visits regularly. Upon seeing their analytics, they decide to abandon uploading recipes and instead only write about Raspberry Pis.\u003c/p\u003e\n\u003cp\u003eWhile this would likely get you more views, the idea is that the personal website then loses some \u003cem\u003epersonal touch\u003c/em\u003e. Some argue that personal websites should not be driven by any agenda other than yourself. No data, no click-bait titles, etc.\u003c/p\u003e\n\u003cp\u003eOf course this goes into why you created your website to begin with? For myself, this website\u0026rsquo;s primary purpose is for \u003cem\u003eme\u003c/em\u003e. I often forget how to perform many tasks and use this website as a reference guide.\u003c/p\u003e\n\u003cp\u003eThough one of my core principles is in sharing knowledge. An important secondary goal of mine is that others find my website useful in some way. The best way to achieve this is to carefully craft each and every one of my blog posts.\u003c/p\u003e\n\u003cp\u003ePersonally, I find this difficult. Striving for perfection in a post often leads to its incompletion. In fact, it\u0026rsquo;s not uncommon for me to sit on a topic for multiple months (sorry \u003ca href=\"https://brandonrozek.com/blog/contributing-openstreetmap/\"\u003eOpenStreetMap\u003c/a\u003e) only to then force myself to write something on the topic to remove it from my backlog.\u003c/p\u003e\n\u003cp\u003eNow I could revisit older posts and spruce them up, but over the years I\u0026rsquo;ve written more than 300 blog posts covering a variety of technical content. It would be a slog to comb over each individual one.\u003c/p\u003e\n\u003cp\u003eThis is where \u003ca href=\"https://brandonrozek.com/blog/goaccess/\"\u003eanalytics come into play for me\u003c/a\u003e. This allows me to identify popular blog posts and spruce up those instead. It does mean that unpopular blog posts are unlikely to get corrected\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e. However since time is limited, it\u0026rsquo;s nice to know that I\u0026rsquo;m improving a post that many look at and potentially rely on.\u003c/p\u003e\n\u003cp\u003eAlso throughout the years, I\u0026rsquo;ve edited the engine that creates these posts. This means that there are some broken links in the Internet that gets surfaced every now and again in my error logs :)\u003c/p\u003e\n\u003cp\u003eI understand that to some looking at an analytics dashboard can cause \u003cem\u003efixation\u003c/em\u003e. That doesn\u0026rsquo;t mean, however, that we should dismiss analytics entirely. It\u0026rsquo;s a tool, and if there\u0026rsquo;s some benefit and it\u0026rsquo;s right for you, then we should make use of it.\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eI also improve posts as I reference them personally. Some people email me with questions and provide suggestions which eventually make their way to the blog posts as well.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2023.05.17",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/syndicating-hugo-to-medium/",
    "url": "https://brandonrozek.com/blog/syndicating-hugo-to-medium/",
    "title": "Syndicating Hugo Posts to Medium",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cem\u003eWarning: This work was done prior to the deprecation of the Medium API. I\u0026rsquo;m unsure how much longer the contents of this post will be relevant. Regardless, the ideas in this post can be adapted to other syndication systems of Hugo content.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eI wrote a script that can selectively upload blog posts generated from a Hugo website onto Medium. This post will go into further detail each of the following steps that make it happen:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eCreating labels on the YAML frontmatter\u003c/li\u003e\n\u003cli\u003eOutputting a JSON feed to make it easier to create scripts\u003c/li\u003e\n\u003cli\u003eWriting a python script to ingest the JSON feed and upload new specified posts onto Medium\u003c/li\u003e\n\u003cli\u003eUpdating the YAML frontmatter with information from Medium\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eStep 1 Define Labels:\u003c/strong\u003e This advice not only goes for Medium but any other site you want to syndicate to. Within each of the content files within Hugo, there\u0026rsquo;s generally a frontmatter section that holds the metadata such as title, date, and tags. Within the YAML frontmatter, I created two new fields:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003emedium_enabled: Whether or not I want this post to be uploaded to Medium\u003c/li\u003e\n\u003cli\u003emedium_post_id: The post identifier used within Medium, empty if non-existent.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWe don\u0026rsquo;t need to include these two new fields in every new post we create. Instead in Step 2, we\u0026rsquo;ll consider some sane defaults. Though it could be useful to include some default values within the \u003ca href=\"https://gohugo.io/content-management/archetypes/\"\u003earchetype\u003c/a\u003e file so that you don\u0026rsquo;t forget they exist.\u003c/p\u003e\n\u003cp\u003eFor example:\u003ccode\u003ethemes/themeName/archetypes/default.md\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e---\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003etitle\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ replace .TranslationBaseName \u0026#34;\u003c/span\u003e-\u003cspan style=\"color:#e6db74\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; | title }}\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003edate\u003c/span\u003e: {{ \u003cspan style=\"color:#ae81ff\"\u003edateFormat \u0026#34;2006-01-02T15:04:05Z07:00\u0026#34; .Date }}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003edraft\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003etags\u003c/span\u003e: []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003emedium_enabled\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e---\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eStep 2 Setup JSON Feed:\u003c/strong\u003e Following the \u003ca href=\"https://gohugo.io/templates/\"\u003eHugo template system\u003c/a\u003e, I created a \u003ccode\u003eitem.json.json\u003c/code\u003e, \u003ccode\u003elist.json.json\u003c/code\u003e, and \u003ccode\u003esingle.json.json\u003c/code\u003e. For this post, the first two are important.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003elist.json.json\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;version\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://jsonfeed.org/version/1.1\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;title\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ if eq  .Title  .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} | {{ end }}{{ .Site.Title }}{{ end }}\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;home_page_url\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .Site.BaseURL }}\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;feed_url\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .Permalink }}\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;description\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .Description }}\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .Site.BaseURL }}img/{{ .Site.Params.avatar }}\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;language\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;en-US\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;authors\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{with\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e$.Site.Params.name\u003c/span\u003e }\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            { \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ . }}\u0026#34;\u003c/span\u003e }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003eend\u003c/span\u003e }\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;items\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003erange\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e$index,\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e$page\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e:=\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e.Pages\u003c/span\u003e }\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e{\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003ene\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e$index\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e0\u003c/span\u003e }\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e,{\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003eend\u003c/span\u003e }\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e.Render\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026#34;item\u0026#34;\u003c/span\u003e}\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003eend\u003c/span\u003e }\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eMy \u003ccode\u003elist.json.json\u003c/code\u003e follows the \u003ca href=\"https://jsonfeed.org/\"\u003eJSON Feed\u003c/a\u003e specification. The idea here is that this file will contain a list of blog posts within the \u003ccode\u003eitems\u003c/code\u003e attribute. For each item, we have the next template file that defines how it\u0026rsquo;s structured.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eitem.json.json\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .Permalink }}\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;url\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .Permalink }}\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;title\u0026#34;\u003c/span\u003e: {\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e.Title\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003ejsonify\u003c/span\u003e }}\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;authors\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e:\u003c/span\u003e [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{with\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e$.Site.Params.Author\u003c/span\u003e }\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            { \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ . }}\u0026#34;\u003c/span\u003e }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003eend\u003c/span\u003e }\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ]\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;content_html\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e:\u003c/span\u003e {\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e.Content\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003ejsonify\u003c/span\u003e }\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;date_published\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .Date.Format \u0026#34;\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e2006.01\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e.\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e02\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34; }}\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;tags\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e:\u003c/span\u003e {\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e.Params.tags\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003ejsonify\u003c/span\u003e }\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;_syndication\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e:\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;medium\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;enabled\u0026#34;\u003c/span\u003e: {\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e.Params.medium_enabled\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003edefault\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026#34;false\u0026#34;\u003c/span\u003e }},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;post_id\u0026#34;\u003c/span\u003e: {\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e.Params.medium_post_id\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003ejsonify\u003c/span\u003e }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNotice the \u003ccode\u003e_syndication\u003c/code\u003e attribute. Attributes beginning with an underscore are ignored by JSON feed readers. Within this attribute, we can store our \u003ccode\u003eenabled\u003c/code\u003e and \u003ccode\u003epost_id\u003c/code\u003e attributes. If the post does not specify the relevant attributes in the frontmatter YAML, we\u0026rsquo;ll assume \u003ccode\u003efalse\u003c/code\u003e for \u003ccode\u003eenabled\u003c/code\u003e and \u003ccode\u003enull\u003c/code\u003e to for \u003ccode\u003epost_id\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFinally for this step we need to tell Hugo to generate this JSON feed. Within your website\u0026rsquo;s \u003ccode\u003econfig.toml\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# ...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#a6e22e\"\u003eoutputFormats\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ejson\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;json\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003emediaType\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;application/feed+json\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003ebaseName\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;index\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003eisPlainText\u003c/span\u003e = \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003erel\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;alternate\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003eisHTML\u003c/span\u003e = \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003enoUgly\u003c/span\u003e = \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003epermalinkable\u003c/span\u003e = \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#a6e22e\"\u003eoutputs\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003esection\u003c/span\u003e = [\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;json\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;html\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;rss\u0026#34;\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e# ...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eStep 3 Ingest Feed and Syndicate via External Script:\u003c/strong\u003e Now that we can store the metadata on whether or not we want to syndicate a particular post and its relevant medium id if it has already been, we can create an external script that ingests this data and uploads if necessary.\u003c/p\u003e\n\u003cp\u003eWe\u0026rsquo;ll use Python for this task. The core loop of the file will look like the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Generate the necessary feed files\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esubprocess\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003erun([\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;hugo\u0026#39;\u003c/span\u003e], check\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eTrue\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Grab blog\u0026#39;s feed\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edata \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e open(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;public/blog/index.json\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;r\u0026#34;\u003c/span\u003e, encoding\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;UTF-8\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e feed_file:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    data \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e feed_file\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eread()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efeed_data \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e json\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eloads(data)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Go through each post and check syndication status\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e post \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e feed_data[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;items\u0026#39;\u003c/span\u003e]:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    medium_enabled \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e post[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;_syndication\u0026#39;\u003c/span\u003e][\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;medium\u0026#39;\u003c/span\u003e][\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;enabled\u0026#39;\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    medium_post_id \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e post[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;_syndication\u0026#39;\u003c/span\u003e][\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;medium\u0026#39;\u003c/span\u003e][\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;post_id\u0026#39;\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e medium_enabled \u003cspan style=\"color:#f92672\"\u003eand\u003c/span\u003e medium_post_id \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# Syndicate.....\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        medium_result \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e syndicate_post(post)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        update_front_matter(post, medium_result)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        time\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esleep(\u003cspan style=\"color:#ae81ff\"\u003e60\u003c/span\u003e) \u003cspan style=\"color:#75715e\"\u003e# Throttle to not hit rate limit\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe last couple steps were necessary for Hugo to produce the JSON feed needed which contains the appropriate syndication fields. Now the magic is within the \u003ccode\u003esyndicate_post\u003c/code\u003e method.\u003c/p\u003e\n\u003cp\u003eFor the magic we need to make use of the \u003ca href=\"https://github.com/Medium/medium-api-docs\"\u003eMedium API\u003c/a\u003e. You\u0026rsquo;ll need to know both your \u003ccode\u003eaccess token\u003c/code\u003e and your \u003ccode\u003eauthorId\u003c/code\u003e. Your author id can be displayed publically, for example mine is \u003ccode\u003e18bad95d2020608a45ef502ef0db83d2cad2e28886d8d3eeef71a6bd089fc2a4e\u003c/code\u003e. However, your access token must be kept private. Otherwise, others can impersonate you. For my script, I put my access token within a secret file that I don\u0026rsquo;t commit to the repository.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Grab secret\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emedium_secret \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e open(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;secrets/medium.secret\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;r\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e secret_file:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    medium_secret \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e secret_file\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eread()\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003estrip()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe secret is used within the request headers\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erequest_headers \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Host\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;api.medium.com\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Authorization\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003ef\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Bearer \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{\u003c/span\u003emedium_secret\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Content-Type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;application/json\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Accept\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;application/json\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Accept-Charset\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;utf-8\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFollowing the API documentation we then create a JSON object that represents our new blog post with its corresponding metadata.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Structure API Request\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eblog_post \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e dict(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    title\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003epost[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;title\u0026#39;\u003c/span\u003e],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    contentFormat\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;html\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    content\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003epost[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;content_html\u0026#39;\u003c/span\u003e],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    tags\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003epost[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;tags\u0026#39;\u003c/span\u003e],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    canonicalUrl\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003epost[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;url\u0026#39;\u003c/span\u003e],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    publishStatus\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;public\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    license\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;all-rights-reserved\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    notifyFollowers\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eFalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou\u0026rsquo;re welcome to change the publish status, license, and follower notification as you see fit. Afterwards, following the API documentation we send a \u003ccode\u003ePOST\u003c/code\u003e request to the Medium API\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Send Request\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econn \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e HTTPSConnection(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;api.medium.com\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econn\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003erequest(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;POST\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003ef\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/v1/users/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{\u003c/span\u003eAUTHOR_ID\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/posts\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    json\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003edumps(blog_post)\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eencode(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;utf-8\u0026#34;\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    request_headers\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe\u0026rsquo;ll need to hold onto the response from the website as that\u0026rsquo;ll contain our post id.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check and parse response\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eresponse \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e conn\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003egetresponse()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e response\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003estatus \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e201\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eException\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003ef\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Medium API rejected the request with response code \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{\u003c/span\u003eresponse\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003estatus\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emedium_response \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e json\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eloads(response\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eread()\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003edecode(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;utf-8\u0026#39;\u003c/span\u003e))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econn\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eclose()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eStep 4 Update Frontmatter:\u003c/strong\u003e Now that we have the post id within the Medium response, we can look towards the original markdown file that contains our content and modify the frontmatter so that the \u003ccode\u003emedium_post_id\u003c/code\u003e field is populated.\u003c/p\u003e\n\u003cp\u003eFirst we need to get the appopriate markdown file\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Figure out the file path of the post\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eORIG_URL \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e urlparse(post[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;id\u0026#39;\u003c/span\u003e])\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efile_path \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;content\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e ORIG_URL\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath[:\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;.md\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUsing the \u003ca href=\"https://pypi.org/project/frontmatter/\"\u003eFrontmatter\u003c/a\u003e Python library, we can read the existing YAML frontmatter and edit the post id with what we received from Medium\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Read existing frontmatter and edit the post id\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eitem \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e {}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e open(file_path, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;r\u0026#34;\u003c/span\u003e, encoding\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;UTF-8\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e content_file:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    item \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e frontmatter\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eload(content_file)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    item[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;medium_post_id\u0026#39;\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e medium_data[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;data\u0026#39;\u003c/span\u003e][\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;id\u0026#39;\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe library also helps us edit the file with updated fronmatter\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Write out new frontmatter\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e open(file_path, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;w\u0026#34;\u003c/span\u003e, encoding\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;UTF-8\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e content_file:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    content_file\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ewrite(frontmatter\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003edumps(item))\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eConclusion:\u003c/strong\u003e This constitutes my somewhat generic framework for how you can selectively syndicate content within a Hugo website. To summarize, we do this by keeping track of the syndication through frontmatter additions and a JSON feed. We then created an external script that ingests this feed and syndicates if necessary, finally updating the original frontmatter with the server response.\u003c/p\u003e\n\u003cp\u003eDownsides to this approach is that the external script occurs outside of the \u003ccode\u003ehugo\u003c/code\u003e call. This adds an extra layer of complication when managing your website. However, keeping it all within Hugo is not only difficult but inflexible. The way I envision this is that Hugo is a database of sorts that keeps track of the syndication status and external scripts operates over this database.\u003c/p\u003e\n\u003cp\u003eHow do you go about syndicating content to other websites? Is your approach different from mine? Please get in touch and share :)\u003c/p\u003e\n\u003cp\u003eRelevant files from my website repository:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://github.com/Brandon-Rozek/website/blob/52b47f1311e7bf98b97c69bd0e838d8e3cdf8392/scripts/syndicate_medium.py\"\u003ehttps://github.com/Brandon-Rozek/website/blob/52b47f1311e7bf98b97c69bd0e838d8e3cdf8392/scripts/syndicate_medium.py\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://github.com/Brandon-Rozek/website/blob/52b47f1311e7bf98b97c69bd0e838d8e3cdf8392/config.toml\"\u003ehttps://github.com/Brandon-Rozek/website/blob/52b47f1311e7bf98b97c69bd0e838d8e3cdf8392/config.toml\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://github.com/Brandon-Rozek/website-theme/blob/fb42d6b950b1d7752e199dc16fd0d39292a16c93/layouts/_default/item.json.json\"\u003ehttps://github.com/Brandon-Rozek/website-theme/blob/fb42d6b950b1d7752e199dc16fd0d39292a16c93/layouts/_default/item.json.json\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://github.com/Brandon-Rozek/website-theme/blob/fb42d6b950b1d7752e199dc16fd0d39292a16c93/layouts/_default/list.json.json\"\u003ehttps://github.com/Brandon-Rozek/website-theme/blob/fb42d6b950b1d7752e199dc16fd0d39292a16c93/layouts/_default/list.json.json\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n",
    "date_published": "2023.05.17",
    "tags": ["Hugo"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/avoid-mutating-set-items-python/",
    "url": "https://brandonrozek.com/blog/avoid-mutating-set-items-python/",
    "title": "Avoid Mutating Items within a Set (Python Edition)",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhenever we create a set of objects, there\u0026rsquo;s an implicit assumption that those objects don\u0026rsquo;t change. What happens when we break this implicit contract and mutate items within a set? If negative properties occur, how can we get around this issue? We\u0026rsquo;ll explore this through the lens of set membership and talk about how the hash method and equality method are used within sets.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eExample\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eWe start by defining a Person class that holds a name\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ePerson\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __init__(self, name):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e name\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn order to put it within a set, we need to define a hash method.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ePerson\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __init__(self, name):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e name\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __hash__(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e hash((\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Person\u0026#34;\u003c/span\u003e, self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epeople \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Clare\u0026#34;\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;James\u0026#34;\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;John\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e} \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe property we\u0026rsquo;ll focus on in this post is set membership. In other words, does the set contain a specified object?\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e people) \u003cspan style=\"color:#75715e\"\u003e# False\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis might seem strange since we can clearly see a \u003ccode\u003ePerson(\u0026quot;Brandon\u0026quot;)\u003c/code\u003e in the set! The problem is that we didn\u0026rsquo;t override the \u003ccode\u003e__eq__\u003c/code\u003e method within the class. Currently it only considers two \u003ccode\u003ePerson\u003c/code\u003e objects the same if they share the same \u003cem\u003ereference\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eTo test \u003ccode\u003ePerson\u003c/code\u003e equivalence in a more intuitive way, we\u0026rsquo;ll say that two \u003ccode\u003ePerson\u003c/code\u003e objects are equivalent if they share the same name.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ePerson\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __init__(self, name):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e name\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __hash__(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e hash((\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Person\u0026#34;\u003c/span\u003e, self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \t\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __eq__(self, other):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e isinstance(other, Person) \u003cspan style=\"color:#f92672\"\u003eand\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e other\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e people) \u003cspan style=\"color:#75715e\"\u003e# True\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow let\u0026rsquo;s mutate the names so they all start with \u003ccode\u003eAwesome \u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e p \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e people:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    p\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Awesome \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e p\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLet\u0026rsquo;s test for membership again\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Awesome Brandon\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e people) \u003cspan style=\"color:#75715e\"\u003e# False\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e people) \u003cspan style=\"color:#75715e\"\u003e# False\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhat happened? Why is both \u003ccode\u003eAwesome Brandon\u003c/code\u003e and \u003ccode\u003eBrandon\u003c/code\u003e not in the set? This has to do with the way that the sets keep track of the objects within them.\u003c/p\u003e\n\u003cp\u003eWhen an object is inserted, it\u0026rsquo;s placed into bins according to their hash. Therefore, when checking for set membership, we look to see if there are items within that hash bin.\u003c/p\u003e\n\u003cp\u003eOriginally when we added \u003ccode\u003ePerson(\u0026quot;Brandon\u0026quot;)\u003c/code\u003e the following hash is computed:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(hash((\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Person\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e))) \u003cspan style=\"color:#75715e\"\u003e# 8879556234447129293\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe object \u003ccode\u003ePerson(\u0026quot;Brandon\u0026quot;)\u003c/code\u003e is then placed in bin 8879556234447129293.\u003c/p\u003e\n\u003cp\u003eWhen we mutate the object, we don\u0026rsquo;t remove the item from the set and add it back in with a recomputed hash. Instead the mutated object stays within the same hash bin.\u003c/p\u003e\n\u003cp\u003eSo what happens then when we ask if \u003ccode\u003ePerson(\u0026quot;Awesome Brandon\u0026quot;)\u003c/code\u003e is within the set? First, it checks to see if there are any non-empty bins with the same hash.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(hash((\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Person\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Awesome Brandon\u0026#34;\u003c/span\u003e))) \u003cspan style=\"color:#75715e\"\u003e# 8677222781184413080\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOur set contains no items within the 8677222781184413080 hash bin. Therefore, the set membership will immediately fail disregarding completely the \u003ccode\u003e__eq__\u003c/code\u003e method we define.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAside:\u003c/strong\u003e Another implicit contract we have is that for a given hash method, two equivalent objects always produce the same hash. What happens when that\u0026rsquo;s not the case?\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e random\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ePerson\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __init__(self, name):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e name\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __hash__(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# Add randomness!\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e hash((\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Person\u0026#34;\u003c/span\u003e, self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename, random\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003erandint(\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e)))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __eq__(self, other):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e isinstance(other, Person) \u003cspan style=\"color:#f92672\"\u003eand\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e other\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __str__(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ef\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Person(name=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{\u003c/span\u003eself\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e)\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epeople \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Clare\u0026#34;\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;James\u0026#34;\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;John\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e people) \u003cspan style=\"color:#75715e\"\u003e# False\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAgain, the set membership returns false disregarding our \u003ccode\u003e__eq__\u003c/code\u003e method since the \u003ccode\u003e__hash__\u003c/code\u003e outputs don\u0026rsquo;t match.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWorkaround:\u003c/strong\u003e If we can\u0026rsquo;t mutate the items in a set directly, what should we do instead? The solution here is to remove the old items and insert newly created items with the modified data.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epeople \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Clare\u0026#34;\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;James\u0026#34;\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;John\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eto_remove \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e set()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eto_add \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e set()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e p \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e people:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    to_remove\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd(p)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    to_add\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd(Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Awesome \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e p\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epeople \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (people \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e to_remove)\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eunion(to_add)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Awesome Brandon\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e people) \u003cspan style=\"color:#75715e\"\u003e# True\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2023.05.17",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/paying-for-things/",
    "url": "https://brandonrozek.com/blog/paying-for-things/",
    "title": "Why pay for things when you can get it for free?",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOther than physical hardware, it is often expected with technology that services come for free. It should be free to make a search query, free to host gigabytes of personal photos, free to send sticker packs to all my friends. And while I welcome lowering the barrier of entry to use technology, I want to provide a counter narrative. This type of expectation can have negative effects.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAlmost nothing is truly free\u003c/strong\u003e. Outside of technology, it costs money to keep clean and maintain parks. Similarly, a server needs to be available to respond to a search query. In fact these are often multiple servers, each that run on electricity and operate within an air conditioned data center.\u003c/p\u003e\n\u003cp\u003eThere is a common saying among privacy activists:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eIf you are not paying for the product, then you are the product.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eThis has some truth to it. A company can only operate at a loss for so long therefore the easiest way to monetize is through advertisements. For an advertisement to be effective, the company has to collect information about your behavior patterns.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eExpectation of free can drive out competition.\u003c/strong\u003e Who can compete with a company that is offering their services for free? Knowing this, some large companies often operate at a loss for a certain time period. Why? In order to drive out competition. Google at one point offered unlimited photo storage as long as you saved the photo as \u0026ldquo;High Quality\u0026rdquo;. This has since changed in June 2021. This marketing tactic is known as the \u0026ldquo;bait and switch\u0026rdquo;. It relies on the fact that it\u0026rsquo;s often effort for people to change services and that they would instead eat the cost once it\u0026rsquo;s introduced later on.\u003c/p\u003e\n\u003cp\u003eI argue that the lack of competition hinders innovation. Currently Google reigns supreme over search. There\u0026rsquo;s some competition from Bing and other smaller search engines, though the market share isn\u0026rsquo;t enough for Google to try to improve the product.\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003cp\u003eIn a world where large tech companies offer expensive services for free. It is hard for a small company to bootstrap and provide a competitive take in the area.\u003csup id=\"fnref:2\"\u003e\u003ca href=\"#fn:2\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e2\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003cp\u003eWhat about open source? It turns out that there\u0026rsquo;s often a way to financially support these projects. The ways are varied but they include paying for support, hosting, or even solely donations. I argue that if no funding model exists, then it\u0026rsquo;s likely for the project to be unsustainable. At the end of the day humans are behind it all and we all require to have our needs met.\u003c/p\u003e\n\u003cp\u003eThough another interesting component of open source is that it is often driven by the needs of people. Often times when a developer contributes a feature it\u0026rsquo;s because they want to use the feature themselves. In that way they provide value to the project. I encourage people to contribute to the projects they depend on in some way (if possible), whether financially, through documentation, or code.\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eNowadays, it seems that another currently free product ChatGPT is adding competition to the search engine space. Bing is betting on the introduction of large language models into their search engine. It has yet to be shown whether this will be the latest evolution of search.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:2\"\u003e\n\u003cp\u003eKagi is a search engine that is taking on this challenge. They have a small free trial, but otherwise require that people pay for their service. I currently pay and am happy with their product. One argument I heard against it is that it\u0026rsquo;ll never take off and surpass Google. I\u0026rsquo;m curious, does it need to surpass Google to be sustainable?\u0026#160;\u003ca href=\"#fnref:2\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2023.05.08",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/bonds-and-interest-rate-risk/",
    "url": "https://brandonrozek.com/blog/bonds-and-interest-rate-risk/",
    "title": "Bonds and Interest Rate Risk",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eDisclaimer: This post is not investment advice.\u003c/p\u003e\n\u003cp\u003eWhen looking around at investing, I often see people suggest that a person\u0026rsquo;s portfolio should have some percentage in equity (stocks) and some percentage in bonds.  In this post, lets specifically look at the bond piece.\u003c/p\u003e\n\u003cp\u003eWhat is a bond? When an organization issues debt, investors purchase these bonds to provide the organization the principal of the bond. The principal of the bond is equivalent to the purchase price minus any possible fees the broker might impose.\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e The reason investors purchase these bonds is because the organization promises to pay a fixed amount of extra funds on top on a periodic basis. It\u0026rsquo;s often said that bonds are a great way of producing \u003cem\u003efixed income\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s get into specifics with an example. Say that Organization A issues a $1000 bond at a 10% coupon rate with a maturity date of 5 years from now. Typical coupon dates are set semi-annually so for this example the purchaser of the bond would receive $50 twice a year. At the end of the five years, the purchaser would receive the $1000 principal back.\u003c/p\u003e\n\u003cp\u003eThe main risk when it comes to bonds is the possibility that the organization might go bankrupt. This is called \u003cem\u003edefault risk\u003c/em\u003e. If this happens, it does not necessarily mean that the purchaser lose the entire principal of the bond. During the bankruptcy process, the organization\u0026rsquo;s assets are sold to clear out their balance sheet. An estimate of the percent of the principal one might receive back is called the recovery rate.\u003c/p\u003e\n\u003cp\u003eOne can measure default risk by looking at the \u003cem\u003ecredit rating\u003c/em\u003e of the organization that they\u0026rsquo;re considering buying a bond from. Typically bonds with higher yields will have lower credit ratings with the lowest being called \u003cem\u003ejunk bonds\u003c/em\u003e. It\u0026rsquo;s up to the individual investor what their risk versus reward preferences lie.\u003c/p\u003e\n\u003cp\u003eNow when someone says they\u0026rsquo;ve allocated 30% of their portfolio to bonds, they\u0026rsquo;re not suggesting that they individually pick out however many bonds to reach that amount. Instead it\u0026rsquo;s more likely that they\u0026rsquo;re investing in a bond fund. These are generally mutual funds, and they work by pooling together money from multiple investors to buy large amounts of bonds in a variety of different sectors or credit ratings.\u003c/p\u003e\n\u003cp\u003eThere are several benefits to this approach:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eLower barrier of entry: Some bond funds allow the individual investor to put in as little as $1, whereas if the investor wanted to purchase a bond it\u0026rsquo;d often be above $1000.\u003c/li\u003e\n\u003cli\u003eDiversification Included: Companies within the same sector are often correlated with one another. That means that if Microsoft is having a bad year, it is likely that Apple and Google are experiencing the same. This means that you\u0026rsquo;ll often want to make sure you\u0026rsquo;re not buying bonds only within one sector of the economy. Bond funds often keep track of these for you and spreads your risk across multiple sectors.\u003c/li\u003e\n\u003cli\u003eGreater liquidity: Bond funds often allow the investor to buy and sell positions within the fund whenever they\u0026rsquo;d like. If the investor owned individual bonds, it might take some time before another investor is willing to buy it off of you. They often say that municipal bonds are not that liquid.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIs the risk profile the same for bond funds as it is for individual bonds? Generally yes, however, there\u0026rsquo;s one risk that I didn\u0026rsquo;t mention in the last section that has a greater affect in bond funds: Interest or Duration risk.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s continue with our example. Say that the federal reserve increases interest rates and new debt being issued by the organization comes with a 20% coupon rate. Now let\u0026rsquo;s say the prior purchaser wants to sell their 10% coupon rate bond. They might find that they have to sell at a loss.\u003c/p\u003e\n\u003cp\u003e\u0026ldquo;How come?\u0026rdquo; you might ask, don\u0026rsquo;t bonds provide a fixed income? That\u0026rsquo;s only true if the bond is held to maturity. If the purchaser wants to sell the bond before then, then the purchaser is subject to market conditions.  To make the example easier let\u0026rsquo;s say that any coupon payments were made yet.\u003c/p\u003e\n\u003cp\u003ePrior 10% bond coupon payments: ($1000 * .10) /  2 * 10 = $500 income by time of maturity\u003c/p\u003e\n\u003cp\u003eCurrent 20% bond coupon payments: ($1000 * .20) / 2 * 10 = $1000 income by the time of maturity\u003c/p\u003e\n\u003cp\u003eWhy would another investor want to purchase the 10% bond at $1000 if they\u0026rsquo;ll receive $500 less income by the time the bond matures?  The seller would instead have to make up for the difference and sell the bond for $500.\u003c/p\u003e\n\u003cp\u003eThis is the price of liquidity. Bond funds have to be liquid so they often do not hold their bonds to maturity and are subject to market conditions. In the year of the time of writing, 2023, the federal reserve has been consistently raising interest rates. This means that interest rate risk is high during this time period.\u003c/p\u003e\n\u003cp\u003eOne strategy that some funds do, is to only hold short term debt. This makes it easier to either hold to maturity or have the bond mature before interest rates change. As stated in the beginning of the post, this is not investment advice, and I also don\u0026rsquo;t manage money for a living. This post summarizes the different risks I recently learned about bonds. Feel free to email me corrections or concepts to add.\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eThere could also be some fluctuations in the price based on the credit rating of the organization.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2023.05.08",
    "tags": ["Finance"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/contributing-openstreetmap/",
    "url": "https://brandonrozek.com/blog/contributing-openstreetmap/",
    "title": "Contributing to OpenStreetMap",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLast year I made my first foray into contributing to \u003ca href=\"https://www.openstreetmap.org/\"\u003eOpenStreetMap\u003c/a\u003e. I find the concept of contributing data to enhance mapping very appealing as an open source enthusiast. Now when you think about editing a map, staring at satellite imagery and drawing traces may come to mind. But what if I told you that\u0026rsquo;s not the only way to contribute to OpenStreetMap? What if we can both contribute to an open source database of knowledge, and get fresh air at the same time?\u003c/p\u003e\n\u003cp\u003eMobile apps \u003ca href=\"https://wiki.openstreetmap.org/wiki/StreetComplete\"\u003eStreetComplete\u003c/a\u003e and \u003ca href=\"https://wiki.openstreetmap.org/wiki/Vespucci\"\u003eVespucci\u003c/a\u003e make it possible for me to go out to my local community and \u003cem\u003esurvey\u003c/em\u003e my surroundings. In fact, I often upload my edits while I\u0026rsquo;m outside. There\u0026rsquo;s no need to write things down and upload at a later date.\u003c/p\u003e\n\u003cp\u003eThe absolute easiest way to get started is with StreetComplete. It\u0026rsquo;s a great way to start because not only do they simplify the experience, but they also provide gamification elements to help get started.\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e The quests are missing metadata within OpenStreetMap. For example you\u0026rsquo;ll get questions like \u0026ldquo;How many lanes are in this road?\u0026rdquo; or \u0026ldquo;Is this sidewalk lit?\u0026rdquo;.  As you use the app, it also tells you more features of OpenStreetMap and community services over time.\u003c/p\u003e\n\u003cp\u003eSince StreetComplete only provides quests about missing metadata, there will come a time where that feels restrictive.\u003csup id=\"fnref:2\"\u003e\u003ca href=\"#fn:2\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e2\u003c/a\u003e\u003c/sup\u003e For example, what if a shop changed owners recently and changed names? This is where our next app comes in.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve only recently started using Vespucci and from what I can tell you can edit just about anything on the app. This means that it\u0026rsquo;s not as intuitive to pick up as StreetComplete and does have a bit of a learning curve. I recently used Vespucci to add business locations that were not already in OpenStreetMap. I was able to do this by editing the metadata of a node. A node represents a latitude and longitude, but through adding metadata we can insert things like type of business and hours of operation.\u003c/p\u003e\n\u003cp\u003eVespucci also includes presets which help you know which fields to include. For example there\u0026rsquo;s a preset called \u0026ldquo;Restaurant\u0026rdquo; which provides fields such as cuisine, hours of operation, delivery available, and others. I encourage you to try it out and help provide information about your area!\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eFun fact, on my profile at the time of writing, it says I have 46 achievement levels.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:2\"\u003e\n\u003cp\u003eIt appears that recent versions of StreetComplete features \u003cem\u003eoverlays\u003c/em\u003e which helps address some of these concerns. I have yet to try them out, however, I still encourage people trying out Vespucci for advanced editng.\u0026#160;\u003ca href=\"#fnref:2\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2023.05.07",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/tildeverse/",
    "url": "https://brandonrozek.com/blog/tildeverse/",
    "title": "Tildeverse",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen I attended my undergraduate institution every student in the computer science department had an opportunity to create a personal website. To do this you logged onto a specific server, create a \u003ccode\u003epublic_html\u003c/code\u003e folder and drop HTML files within it. They even had support for PHP!\u003c/p\u003e\n\u003cp\u003eNow why would I create this personal website on another server when I already have my own website that I\u0026rsquo;ve been maintaining for the past eight years? Honestly it\u0026rsquo;s a great place for experimentation. This website used to be Wordpress driven. During my undergrad, I played around with \u003ca href=\"https://picocms.org/\"\u003ePico CMS\u003c/a\u003e on the school server\u0026rsquo;s to serve Markdown files via PHP. This inspired this current version of my website which uses Hugo on the backend to compile Markdown to HTML.\u003c/p\u003e\n\u003cp\u003eWebsites created in this way were called Tildespaces. This is named after the fact  that in Unix/Linux you can refer to a user\u0026rsquo;s home directory by prefixing their username with a tilde (\u003ccode\u003e~\u003c/code\u003e). A consequence of this, is that often you would refer to the website as \u003ccode\u003ehttp://example.com/~username\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eNow it wasn\u0026rsquo;t only universities that were offering this service. Communities popped up offering free public ways to create websites and other services. These were referred to as Pubnixes or public unixes. Over time this concept became less prominent as the web became a platform for commercialization. Luckily, there are others like me that have been exposed to this in the past and wanted to keep the idea alive. That\u0026rsquo;s where the Tildeverse comes in.\u003c/p\u003e\n\u003cp\u003eThe \u003ca href=\"https://tildeverse.org/\"\u003eTildeverse\u003c/a\u003e is an informal association of communities that provide people access to create accounts on servers to host websites and other services. On that website you can find many different servers and I encourage you to check each out to see if one calls to you. Similar to trying out various Mastodon servers.\u003c/p\u003e\n\u003cp\u003ePersonally, I created an account on \u003ca href=\"https://tilde.club\"\u003eTilde.club\u003c/a\u003e which hosts my \u003ca href=\"https://tilde.club/~brozek\"\u003eWeb\u003c/a\u003e and \u003ca href=\"gemini://tilde.club/~brozek\"\u003eGemini\u003c/a\u003e pages. I even discovered a way of creating a tildespace at my current university which you can see \u003ca href=\"https://www.cs.rpi.edu/~rozekb/\"\u003ehere\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eOne benefit that this approach gives versus say a GitHub pages implementation is that you get access to a whole Linux environment. Even if you don\u0026rsquo;t use it to make a webpage, it can give valuable experience in interacting with Linux systems.\u003c/p\u003e\n\u003cp\u003eAlso if you create HTML pages on the server, migrating to a different server is as easy as moving the files. No lock-in!\u003c/p\u003e\n\u003cp\u003eI hope this inspires you to try it out. For another old Internet reference, also check out \u003ca href=\"https://neocities.org/\"\u003eNeocities\u003c/a\u003e. That website tries to emulate the Yahoo owned Geocities website that was active between 1994-2009.\u003c/p\u003e\n",
    "date_published": "2023.05.07",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/load-balancing-within-openmp-static-schedules/",
    "url": "https://brandonrozek.com/blog/load-balancing-within-openmp-static-schedules/",
    "title": "Load Balancing within OpenMP Static Schedules",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOpenMP allows C++ developers to create shared memory multi-processing applications.\nThis is particularly beneficial for single program multiple data (SPMD) tasks.\nHowever given a large batch of data to process,\nit is often the case that the data cannot be evenly split among all the available parallel threads.\nThis creates an imbalance on how long each thread takes to finish.\nOne popular is to use a dynamic scheduling algorithm,\nthough this might not be desirable in all cases.\nWhat if we know a unit of work that the idle threads can complete before the other threads finish?\nTo take advantage of this potential opportunity,\nwe need to understand how OpenMP distributes work within static schedules.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s showcase static scheduling through an example.\nImagine we have a vector of integers from 0 to 50 and\nwe wish to find the sum of this vector without using a formula.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003estd\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003evector\u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e numbers(\u003cspan style=\"color:#ae81ff\"\u003e50\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003estd\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eiota(std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003ebegin(numbers), std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eend(numbers), \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSay we have eight threads.\nLet\u0026rsquo;s initialize the data structures that will both keep track of the sum\nand keep track of the assignment of indices to each thread.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e sum \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003estd\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003evector\u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003estd\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eunordered_set\u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u0026gt;\u003c/span\u003e assignments(numThreads, std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eunordered_set\u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e());\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn standard static scheduling, we would write the parallel portion as follows:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#pragma omp parallel for reduction(+:sum)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (size_t i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; i \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e numbers.size(); i\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e threadId \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e omp_get_thread_num();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    assignments[threadId].insert(i);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    sum \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e numbers[i];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf we print out the assignments vector, it would look like the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eThread ID: 0 Allocations: 06 05 04 03 02 01 00 \nThread ID: 1 Allocations: 13 12 11 10 09 08 07 \nThread ID: 2 Allocations: 19 18 17 16 15 14 \nThread ID: 3 Allocations: 25 24 23 22 21 20 \nThread ID: 4 Allocations: 31 30 29 28 27 26 \nThread ID: 5 Allocations: 37 36 35 34 33 32 \nThread ID: 6 Allocations: 43 42 41 40 39 38 \nThread ID: 7 Allocations: 49 48 47 46 45 44 \n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eA couple things to notice:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eThe first two threads have one extra unit of work.\nThis makes sense since if you divide fifty by 8 you would have two remaining.\nIn mathematical speak: \u003ccode\u003esize % numThreads = 2\u003c/code\u003e.\nA generalization of the pidgeon-hole principal.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eThe portions of the array that are divided are all sequential.\nThis is to make use of the principal of locality.\nMemory locations near an access are cached as programs are more likely to access nearby locations.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eTo emulate this, we\u0026rsquo;ll need to determine the start and end indices that each thread would consider.\nTo help out let\u0026rsquo;s introduce two new variables.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e leftOvers \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e numbers.size() \u003cspan style=\"color:#f92672\"\u003e%\u003c/span\u003e numThreads;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e elementsPerThread \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e numbers.size() \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e numThreads;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe rest of this example will be within a parallel block.\nSince we\u0026rsquo;re not going to let OpenMP automatically distribute the work,\nwe\u0026rsquo;ll have to specify the macro a little differently.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eomp_set_dynamic(\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eomp_set_num_threads(numThreads);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#pragma omp parallel reduction(+:sum)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e threadId \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e omp_get_thread_num();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Later code is within here\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe start and end indices need to take into account\nnot only how many elements each thread process,\nbut how many of the prior thread ids have an extra unit of work.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esize_t start;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (threadId \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e leftOvers) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    start \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (threadId \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e elementsPerThread) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e leftOvers;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e} \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    start \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e threadId \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e elementsPerThread \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e threadId;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esize_t end;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (threadId \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e leftOvers) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    end \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (threadId \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e elementsPerThread \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e leftOvers;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e} \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    end \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (threadId \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e elementsPerThread \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e threadId \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow we can ask each thread to iterate over their start and end indices.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (size_t i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e start; i \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e end; i\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    assignments[threadId].insert(i);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    sum \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e numbers[i];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHow do we know if this thread is performing less units of statically allocated work?\nWe check its thread id.\nSince thread ids from 0 to leftOvers get assigned one more unit of work,\nthe thread ids that are larger than leftOvers are free game.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (threadId \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e leftOvers \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e leftOvers \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Do extra work here\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow of course it\u0026rsquo;s up for you to figure out what the right type of extra work is.\nSince if the extra work ends up taking longer than the already allocated work, it\u0026rsquo;ll remain imbalanced.\nThis technique, however, gives you another tool in your parallel computing toolbelt if the dynamic scheduling algorithm is suboptimal for your application.\u003c/p\u003e\n\u003cp\u003eFull load balance code example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;omp.h\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;vector\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;numeric\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;unordered_set\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;iostream\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;iomanip\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e ()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{     \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003evector\u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e numbers(\u003cspan style=\"color:#ae81ff\"\u003e50\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eiota(std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003ebegin(numbers), std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eend(numbers), \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e numThreads \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e8\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e leftOvers \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e numbers.size() \u003cspan style=\"color:#f92672\"\u003e%\u003c/span\u003e numThreads;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e elementsPerThread \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e numbers.size() \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e numThreads;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e sum \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003evector\u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003estd\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eunordered_set\u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u0026gt;\u003c/span\u003e assignments(numThreads, std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eunordered_set\u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e());\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    omp_set_dynamic(\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    omp_set_num_threads(numThreads);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e#pragma omp parallel reduction(+:sum)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e threadId \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e omp_get_thread_num();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        size_t start;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (threadId \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e leftOvers) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            start \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (threadId \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e elementsPerThread) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e leftOvers;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        } \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            start \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e threadId \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e elementsPerThread \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e threadId;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        size_t end;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (threadId \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e leftOvers) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            end \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (threadId \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e elementsPerThread \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e leftOvers;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        } \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            end \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (threadId \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e elementsPerThread \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e threadId \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (size_t i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e start; i \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e end; i\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            assignments[threadId].insert(i);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            sum \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e numbers[i];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (threadId \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e leftOvers \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e leftOvers \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#75715e\"\u003e// Do extra work here\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Print reduced sum\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003ecout \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;sum = \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e sum \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eendl;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Print thread allocations\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (size_t i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; i \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e assignments.size(); i\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003ecout \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Thread ID: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e i \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; Allocations: \u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eauto\u003c/span\u003e identifier : assignments[i]) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003ecout \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003esetfill(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;0\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003esetw(\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e  identifier \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003ecout \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eendl;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003ecout \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Min Allocations per Thread: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e (elementsPerThread) \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eendl; \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003ecout \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Extra: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e (leftOvers) \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eendl;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNote to future self, to compile an OpenMP application you need to specify it within \u003ccode\u003eg++\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eg++ -fopenmp filename.cpp -o executablename\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2023.05.05",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/lean3-tutorial/",
    "url": "https://brandonrozek.com/blog/lean3-tutorial/",
    "title": "Lean 3 Tutorial",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWarning: This post covers Lean 3 which has been deprecated\nin favor of Lean 4. Check out my \u003ca href=\"/blog/lean4-tutorial/\"\u003eupdated tutorial\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eLean is an interactive theorem prover created by Microsoft.\nAs part of the RPI logic group, I gave a tutorial introducing Lean and showcasing how to prove some statements within it.\nThis post aims to cover the concepts I went over at the time and can be used as an initial reference.\u003c/p\u003e\n\u003ch2 id=\"propositional-logic-intuitionist-fragment\"\u003ePropositional Logic (Intuitionist Fragment)\u003c/h2\u003e\n\u003cp\u003eTo prove something in Lean, we need to construct an object of that type.\u003c/p\u003e\n\u003ch3 id=\"conjunctive-introduction\"\u003eConjunctive Introduction\u003c/h3\u003e\n\u003cp\u003eTo start off let us prove the statement $P \\wedge Q$ given both $P$ and $Q$ individually.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_p : p) (H_q : q) : (p ∧ q) := begin\n  show p ∧ q, from and.intro H_p H_q,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis shows an application of conjunctive introduction. We created the object $P \\wedge Q$ by applying the hypothesis \u003ccode\u003eand.intro\u003c/code\u003e to both \u003ccode\u003eH_p\u003c/code\u003e which contains $P$ and \u003ccode\u003eH_q\u003c/code\u003e which contains $Q$.\u003c/p\u003e\n\u003cp\u003eAnother way of going about this proof is by \u003cem\u003etransforming the goal to another equivalent one\u003c/em\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_p : p) (H_q : q) : (p ∧ q) := begin\n  split,\n  {\n    show p, from H_p\n  },\n  {\n    show q, from H_q\n  }\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIn this proof, we make use of the \u003ccode\u003esplit\u003c/code\u003e tactic. Given a conjunctive goal $P \\wedge Q$, this tactic replaces it with two subgoals $P$ and $Q$. We can then apply the hypotheses to solve the problem.\u003c/p\u003e\n\u003ch3 id=\"conjunctive-elimination\"\u003eConjunctive Elimination\u003c/h3\u003e\n\u003cp\u003eGiven the proof of $P \\wedge Q$, we can derive $P$ from it.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_pq : p ∧ q) : p := begin\n  show p, from and.left H_pq,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIn the example above, we apply the inference \u003ccode\u003eand.left\u003c/code\u003e to be able to derive the left side of a conjunct. Similarly to the last inference rule, there is a tactic based way to go about it. Tactic based methods are generally more prevalent within the ITP community.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_pq : p ∧ q) : p := begin\n  cases H_pq with H_p H_q, -- p, q\n  show p, from H_p,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eGiven a conjunctive hypothesis, the \u003ccode\u003ecases\u003c/code\u003e tactic will create two new hypothesis from it. The two labels after the \u003ccode\u003ewith\u003c/code\u003e is to specify the names of these hypotheses. The string \u003ccode\u003e--\u003c/code\u003e denotes the start of a comment; I left a small comment to remind me the ordering of the conjuncts.\u003c/p\u003e\n\u003ch3 id=\"disjunctive-introduction\"\u003eDisjunctive Introduction\u003c/h3\u003e\n\u003cp\u003eGiven any statement $P$, we can introduce an arbitrary formula as a disjunct. More intuitively, if Jarold is above the age of 10, then he is above the age of 10 or he likes lollipops. It does not change the truth value of the statement.\u003c/p\u003e\n\u003cp\u003eThe inference based way of representing this is as follows:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_p : p) : (p ∨ q) := begin \n  show p ∨ q, from or.intro_left q H_p,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIn the more popular tactic based notion. We use the tactic \u003ccode\u003eleft\u003c/code\u003e or \u003ccode\u003eright\u003c/code\u003e to denote which side we\u0026rsquo;re going to attempt to prove. If we\u0026rsquo;re trying to prove $P \\vee Q$ and we have $P$ then we will prove the left side.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_p : p) : (p ∨ q) := begin \n  left,\n  show p, from H_p,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"disjunctive-elimination\"\u003eDisjunctive Elimination\u003c/h3\u003e\n\u003cp\u003eThe form of disjunctive elimination included in Lean is more commonly known as \u003cem\u003eproof by cases\u003c/em\u003e. That is if you know either $A$ or $B$ is true. That is, $A \\vee B$. And, you can derive $C$ from $A$ as well as $C$ from $B$. Then it doesn\u0026rsquo;t matter which of $A$ or $B$ is true, because you can derive $C$ regardless.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://en.wikipedia.org/wiki/Disjunction_elimination\"\u003eWikipedia\u003c/a\u003e has a nice intuitive example:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eIf I\u0026rsquo;m inside, I have my wallet on me.\u003c/p\u003e\n\u003cp\u003eIf I\u0026rsquo;m outside, I have my wallet on me.\u003c/p\u003e\n\u003cp\u003eIt is true that either I\u0026rsquo;m inside or I\u0026rsquo;m outside.\u003c/p\u003e\n\u003cp\u003eTherefore, I have my wallet on me.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eTo achieve this in Lean using the inference based approach.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q r : Prop} (H_pr : p → r) (H_qr : q → r) (H_pq : p ∨ q) : r := begin\n  show r, from or.elim H_pq H_pr H_qr,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAlternatively, via the tactic based approach\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q r : Prop} (H_pr : p → r) (H_qr : q → r) (H_pq : p ∨ q) : r := begin\n  cases H_pq,\n  case or.inl : H_p\n  { -- Assume p\n    show r, from H_pr H_p,\n  },\n  case or.inr : H_q\n  { -- Assume q\n    show r, from H_qr H_q,\n  },\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIn the tactic based method the case labels are optional.\nIf you are using the mathlib library, you can automatically\ngenerate the labels using the \u003ccode\u003epretty_cases\u003c/code\u003e command.\u003c/p\u003e\n\u003ch2 id=\"negation\"\u003eNegation\u003c/h2\u003e\n\u003cp\u003eTraditional interactive theorem provers such as Coq focused on the constructivist approach to theorem proving.\nThis allows them to export proofs as programs in OCaml.\nLean places less of an emphasis on this approach and instead supports the proof by contradiction style\nyou see in classical theorem proving.\u003c/p\u003e\n\u003cp\u003eDuring the tutorial, I decided to make this distinction explicit by making use of the law of excluded middle. By default, Lean uses the axiom of choice which can then be used to derive the law of excluded middle, but I cut that part out for brevity.\u003c/p\u003e\n\u003cp\u003eIn other words, if you want to perform a proof by contradiction, don\u0026rsquo;t use the techniques shown in this section and instead use the \u003ccode\u003eby_contradiction\u003c/code\u003e tactic.\u003c/p\u003e\n\u003cp\u003eDeclare the axiom of law of excluded middle:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eaxiom LEM {p : Prop}: p ∨ ¬ p\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"negation-introduction\"\u003eNegation Introduction\u003c/h3\u003e\n\u003cp\u003eFor negation introduction, let\u0026rsquo;s say we have some proposition $P$. If we can use $P$ to derive a falsity, let\u0026rsquo;s say $Q \\wedge \\neg Q$ then $P$ must be false. That is, we introduce a negation to make it $\\neg P$.\u003c/p\u003e\n\u003cp\u003eThe structure of the proof will a proof by cases on the law of exclude middle.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_pq : p → q) (H_pnq : p → ¬q) : ¬p := begin\n  have H_LEM : p ∨ ¬p := LEM,\n\n  -- Assuming ¬p derive ¬p\n  have H_npp : ¬p → ¬p := by {\n    assume H_np : ¬p,\n    show ¬p, from H_np,\n  },\n  \n  -- Assuming p derive ¬p\n  have H_pp : p → ¬p := by {\n    -- Use hypotheses to obtain q and ¬q\n    assume H_p : p,\n    have H_q : q := H_pq H_p,\n    have H_nq : ¬q := H_pnq H_p,\n\n    -- In Lean ¬q is the same as q → false\n    have H_qf : q → false := H_nq,\n\n    -- We have q, so we can derive a falsity\n    have H_f : false := H_qf H_q,\n\n    -- You can derive anything from false\n    have H_UNUSED: p ∧ ¬p := false.rec (p ∧ ¬p) H_f,\n\n    -- Including what we want, ¬p \n    show ¬p, from false.rec (¬p) H_f,\n  },\n  \n  -- By proof by cases, we derive ¬p\n  show ¬p, from or.elim H_LEM H_pp H_npp,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAlternatively, the tactic based approach\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_pq : p → q) (H_pnq : p → ¬q) : ¬p := begin\n  have H_LEM : p ∨ ¬p := LEM,\n  cases H_LEM,\n  case or.inl : H_p\n  {\n    have H_q : q := H_pq H_p,\n    have H_nq : ¬q := H_pnq H_p,\n    have H_qf : q → false := H_nq,\n    have H_f : false := H_qf H_q,\n    show ¬p, from false.rec (¬p) H_f,\n  },\n  case or.inr : H_np\n  { \n    show ¬p, from H_np,\n  },\nend\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"negation-elimination\"\u003eNegation Elimination\u003c/h3\u003e\n\u003cp\u003eOne common representation of negation elimination is to remove any double negations.\nThat is $\\neg \\neg P$ becomes $P$.\u003c/p\u003e\n\u003cp\u003eWe\u0026rsquo;ll similarly show this by performing a proof by cases on the law of excluded middle.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p: Prop} (H_nnp : ¬¬p) : p := begin\n  have H_LEM : p ∨ ¬p := LEM,\n\n  -- Assuming p derive p\n  have H_pp : p → p := by {\n    assume H_p : p,\n    show p, from H_p,\n  },\n\n  -- Assuming ¬p derive p\n  have H_npp : ¬p → p := by {\n    assume H_np : ¬p,\n\n    -- Recall ¬p is the same as p → false\n    have H_pff : (p → false) → false := H_nnp,\n    have H_pf : p → false := H_np,\n\n    -- Apply conditional elimination or modus ponens\n    have H_f : false := H_pff H_pf,\n\n    -- Derive our goal from a falsity\n    show p, from false.rec p H_f,\n  },\n\n  -- By proof by cases, we derive p\n  show p, from or.elim H_LEM H_pp H_npp,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAlternatively for the tactic based approach.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p: Prop} (H_nnp : ¬¬p) : p := begin\n  have H_LEM : p ∨ ¬p := LEM,\n  cases H_LEM,\n  case or.inl : H_p\n  { \n    show p, from H_p,\n  },\n  case or.inr : H_np\n  { \n    have H_pff : (p → false) → false := H_nnp,\n    have H_pf : p → false := H_np,\n    have H_f : false := H_pff H_pf,\n    show p, from false.rec p H_f,\n    \n  },\nend\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"first-order\"\u003eFirst Order\u003c/h2\u003e\n\u003cp\u003eLean is also capable of reasoning over first order logic. In this section, we\u0026rsquo;ll start seeing objects/terms and predicates instead of just propositions.\u003c/p\u003e\n\u003cp\u003eFor example \u003ccode\u003e{α : Type} {P : α → Prop} \u003c/code\u003e means that $P$ is a predicate of arity one and takes an object of type $\\alpha$.\u003c/p\u003e\n\u003ch3 id=\"forall-elim\"\u003eForall Elim\u003c/h3\u003e\n\u003cp\u003eIf we have a forall statement, then we can replace the bound variable with an object of that type and remove the forall. Lets say for our example we have the following forall statement: $\\forall x \\in \\mathbb{N}: x \\ge 0$. Then we can replace the $x$ with $2$ and get the following formula: $2 \\ge 0$.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {α : Type} {P : α → Prop} {y : α} (H : ∀ x : α,  P(x)) : P(y) := begin\n  show P y, from H y,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"forall-intro\"\u003eForall Intro\u003c/h3\u003e\n\u003cp\u003eTo show that some property holds for all $x$ of a certain type, you need to show that it holds\nfor an arbitrary $x$ of that type. We can introduce this object, by the command \u003ccode\u003eassume x\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {α : Type} {P Q R : α → Prop} (H_pq : ∀ x : α, P x → Q x) (H_qr : ∀ x : α, Q x → R x) : ∀ x : α, P x → R x := begin\n  assume x,\n  have H_pqx : P x → Q x := H_pq x,\n  have H_qrx : Q x → R x := H_qr x,\n  assume H_px : P x,\n  have H_qx : Q x := H_pqx H_px,\n  show R x, from H_qrx H_qx,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"exists-intro\"\u003eExists Intro\u003c/h3\u003e\n\u003cp\u003eTo introduce an existential, you need to show that the formula holds for any object of a certain type.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {α : Type} {P : α → Prop} {y : α} (H: P y) : ∃ x: α, P x := begin\n  show ∃ x: α, P x, from exists.intro y H,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIn the tactic based approach, this is done via \u003ccode\u003eexistsi\u003c/code\u003e:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {α : Type} {P : α → Prop} {y : α} (H: P y) : ∃ x: α, P x := begin\n  existsi y,\n  show P y, from H,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"exists-elim\"\u003eExists Elim\u003c/h3\u003e\n\u003cp\u003eLets say we have the following forall statement: $\\forall a \\in \\alpha: p a \\implies b$.\u003c/p\u003e\n\u003cp\u003eNow lets say we have the following existential: $\\exists x, p x$.\u003c/p\u003e\n\u003cp\u003eUsing these, we can derive $b$.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {α : Type} {p : α → Prop} {b : Prop} (H_epx : ∃ x, p x) (H_pab : ∀ (a : α), p a → b) : b := begin\n  show b, from exists.elim H_epx H_pab,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAlternatively for the tactic based approach:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {α : Type} {p : α → Prop} {b : Prop} (H_epx : ∃ x, p x) (H_pab : ∀ (a : α), p a → b) : b := begin\n  cases H_epx with x H_px,\n  have H_pxb := H_pab x,\n  show b, from H_pxb H_px,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"inductive-types\"\u003eInductive Types\u003c/h2\u003e\n\u003cp\u003eOne of the biggest use cases of an interactive\ntheorem prover is in program verification.\nTo help represent recursive data structures, we\nhave the notion of an \u003cem\u003einductive type\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s create a custom representation of a list.\nA list can either by empty (\u003ccode\u003ecnil\u003c/code\u003e) or be an element \u003ccode\u003ehd\u003c/code\u003e combined with the rest of some list \u003ccode\u003etl\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003einductive CustomList (T : Type)\n| cnil : CustomList\n| ccons (hd : T) (tl : CustomList) : CustomList\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eSome examples of a list here include \u003ccode\u003ecnil\u003c/code\u003e,\n\u003ccode\u003eccons(0, cnil)\u003c/code\u003e, and \u003ccode\u003eccons(1, ccons(0, cnil))\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFor convenience, we\u0026rsquo;ll open the \u003ccode\u003eCustomList\u003c/code\u003e namespace\nso that we don\u0026rsquo;t have to refer each constructor\n(\u003ccode\u003ecnil\u003c/code\u003e/\u003ccode\u003eccons\u003c/code\u003e) by it.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eopen CustomList\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"functions-over-inductive-types\"\u003eFunctions over Inductive Types\u003c/h3\u003e\n\u003cp\u003eTo define a function over an inductive type,\nwe need to cover each of the constructors.\u003c/p\u003e\n\u003cp\u003eFor example, let\u0026rsquo;s consider the notion of a list\u0026rsquo;s\nlength.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eIf the list is \u003ccode\u003ecnil\u003c/code\u003e then the length is $0$.\u003c/li\u003e\n\u003cli\u003eIf the list starts with \u003ccode\u003eccons\u003c/code\u003e then we add 1 to the length of the tail \u003ccode\u003etl\u003c/code\u003e.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003edef clength {α : Type}:  CustomList α → ℕ \n  | cnil := 0\n  | (ccons b as) := 1 + clength as\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe can see the output of a function via the \u003ccode\u003e#eval\u003c/code\u003e command.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003e#eval @cnil nat\n#eval clength (ccons 2 (ccons 1 cnil))\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFor another example, let us look at appending two lists. As an example if we have the list \u003ccode\u003e[1, 2, 3]\u003c/code\u003e and the list \u003ccode\u003e[4, 5, 6]\u003c/code\u003e, then appending those two lists will create \u003ccode\u003e[1, 2, 3, 4, 5, 6]\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003edef cappend {α : Type} : CustomList α → CustomList α → CustomList α\n  | cnil         bs := bs\n  | (ccons a as) bs := ccons a (cappend as bs)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eExample evaluations, make sure these come out to what you expect.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003e#eval cappend (ccons 1 cnil) (ccons 2 cnil)\n#eval clength (cappend (ccons 1 cnil) (ccons 2 cnil))\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"theorems-over-inductive-types\"\u003eTheorems over Inductive Types\u003c/h3\u003e\n\u003cp\u003eNow that we have a data structure and some methods over it. We can now prove some interesting properties.\u003c/p\u003e\n\u003cp\u003eFirst let\u0026rsquo;s start off with the following theorem.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eAppending \u003ccode\u003ecnil\u003c/code\u003e to a list \u003ccode\u003eas\u003c/code\u003e is equivalent to the list \u003ccode\u003eas\u003c/code\u003e.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eWhen instantiating an inductive type, the inductive hypothesis is created for it via \u003ccode\u003erec_on\u003c/code\u003e.\nTherefore, we can rely on that for the proof.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem append_nil2 {α : Type} (as : CustomList α) : cappend as cnil = as := begin\n  -- Base Case\n  have H_base : cappend cnil cnil = (@cnil α) := by {\n    rewrite [cappend],\n  },\n\n  -- Inductive Step\n  have H_ind : ∀ (hd : α) (tl : CustomList α), cappend tl cnil = tl → cappend (ccons hd tl) cnil = ccons hd tl\n := by {\n    assume hd : α,\n    assume tl : CustomList α,\n    assume H : cappend tl cnil = tl,\n    calc\n      cappend (ccons hd tl) cnil = ccons hd (cappend tl cnil) : by rewrite [cappend]\n      ... = ccons hd tl : by rewrite H,\n  },\n\n  -- Apply induction principle\n  show cappend as cnil = as, from CustomList.rec_on as H_base H_ind,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe \u003ccode\u003erewrite\u003c/code\u003e command allows us to replace instances of functions with their definitions. The goal is to get both sides of the equality to be syntactically the same.\nThe \u003ccode\u003ecalc\u003c/code\u003e environment allows us to perform multiple rewrites in order to get closer to that end.\u003c/p\u003e\n\u003cp\u003eInstead of explicitly making use of \u003ccode\u003erec_on\u003c/code\u003e, we can use the \u003ccode\u003einduction\u003c/code\u003e tactic.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem append_nil2 {α : Type} (as : CustomList α) : cappend as cnil = as := begin\n  induction as,\n  case CustomList.cnil\n  { \n    show cappend cnil cnil = cnil, from by rewrite [cappend],\n  },\n  case CustomList.ccons : hd tl ih\n  {\n    -- Great for equational rewrite proofs\n    calc\n      cappend (ccons hd tl) cnil = ccons hd (cappend tl cnil) : by rewrite [cappend]\n      ...                        = ccons hd tl : by rewrite [ih]\n    ,\n  },\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eLean also supports writing the theorems in a similar inductive syntax as the definitions. Though to me it looks slightly confusing.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem append_nil3 {α : Type} : ∀ as : CustomList α, cappend as cnil = as\n| (@cnil α) := show cappend cnil cnil = cnil, from by rewrite [cappend]\n| (ccons hd tl) := show cappend (ccons hd tl) cnil = (ccons hd tl), from by {\n    calc\n      cappend (ccons hd tl) cnil = ccons hd (cappend tl cnil) : by rewrite [cappend]\n      ...                        = ccons hd tl : by rewrite append_nil3\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"double-induction\"\u003eDouble Induction\u003c/h3\u003e\n\u003cp\u003eFor our next example, we\u0026rsquo;ll need to perform induction on two lists.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eGiven two lists \u003ccode\u003eas\u003c/code\u003e and \u003ccode\u003ebs\u003c/code\u003e. The length of their append is the same as the length of each individual list added together.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003e\ntheorem length_append_sum {α : Type} (as bs : CustomList α) : clength (cappend as bs) = clength as + clength bs := begin\n  induction as with aa ab ac,\n  induction bs with ba bb bc,\n  case CustomList.cnil\n  {     \n    calc\n      clength (cappend cnil cnil) = clength (@cnil α) : by rewrite [cappend]\n      ...                         = 0 : by rewrite [clength]\n      ...                         = 0 + 0 : by linarith\n      ...                         = clength cnil + clength cnil : by refl,\n  \n  },\n  case CustomList.ccons : bs_hd bs_tl bs_ih\n  { \n    calc\n      clength (cappend (ccons bs_hd bs_tl) bs) = clength (ccons bs_hd (cappend bs_tl bs)) : by rewrite [cappend]\n      ... = 1 + clength (cappend bs_tl bs) : by rewrite [clength]\n      ... = 1 + (clength bs_tl + clength bs) : by rewrite bs_ih\n      ... = (1 + clength bs_tl) + clength bs : by linarith\n      ... = clength (ccons bs_hd bs_tl) + clength bs : by rewrite [clength],\n  },\n  case CustomList.ccons : bs_hd bs_tl bs_ih\n  { \n    calc\n      clength (cappend cnil (ccons bs_hd bs_tl)) = clength (ccons bs_hd bs_tl) : by rewrite [cappend]\n      ... = 0 + clength (ccons bs_hd bs_tl) : by linarith\n      ... = clength cnil + clength (ccons bs_hd bs_tl) : by refl,\n  },\n  -- It intelligently wlogs the fourth case\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIn the alternative inductive style:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem length_append_sum2 {α : Type}: ∀ (as bs : CustomList α), clength (cappend as bs) = clength as + clength bs\n| cnil cnil := by {\n      calc\n      clength (cappend cnil cnil) = clength (@cnil α) : by rewrite [cappend]\n      ...                         = 0 : by rewrite [clength]\n      ...                         = 0 + 0 : by linarith\n      ...                         = clength cnil + clength cnil : by refl,\n}\n| (ccons hd tl) bs := by {\n      calc\n      clength (cappend (ccons hd tl) bs) = clength (ccons hd (cappend tl bs)) : by rewrite [cappend]\n      ... = 1 + clength (cappend tl bs) : by rewrite [clength]\n      ... = 1 + (clength tl + clength bs) : by rewrite length_append_sum2\n      ... = (1 + clength tl) + clength bs : by linarith\n      ... = clength (ccons hd tl) + clength bs : by rewrite [clength],\n}\n| cnil (ccons hd tl) := by {\n      calc\n      clength (cappend cnil (ccons hd tl)) = clength (ccons hd tl) : by rewrite [cappend]\n      ... = 0 + clength (ccons hd tl) : by linarith\n      ... = clength cnil + clength (ccons hd tl) : by refl,\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNotice that a couple times in the proof, make use of a tactic called \u003ccode\u003elinarith\u003c/code\u003e. This stands for \u0026ldquo;linear arithmetic\u0026rdquo; and helps solve goals involving numbers. I generally employ this rather than figuring out which definitions I need to perform commutativity and associativity rules.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eThat concludes the examples I gave for my talk. For each proof, you can see that I included at least two different ways of going about proving it. I generally prefer the inference style method where we explicitly call on \u003ccode\u003eOr.elim\u003c/code\u003e and the like. Though when it comes to double induction, I have not figured out how to apply \u003ccode\u003erec_on\u003c/code\u003e multiple times in a clean way.\u003c/p\u003e\n\u003cp\u003eIn the process of making this tutorial, I released other lean posts. One that I recommend checking out is \u003ca href=\"/blog/readable-lean3-proofs/\"\u003e\u0026ldquo;Readable Lean 3 Proofs\u0026rdquo;\u003c/a\u003e. In it, I give my opinions on how to make the written out proofs more human friendly.\u003c/p\u003e\n",
    "date_published": "2023.03.27",
    "tags": ["Formal Methods"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/implications-prenex-normal-form/",
    "url": "https://brandonrozek.com/blog/implications-prenex-normal-form/",
    "title": "Prenex Normal Form - Implication Exercise",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI recently read through the Wikipedia article on \u003ca href=\"https://en.wikipedia.org/wiki/Prenex_normal_form\"\u003ePrenex Normal Form\u003c/a\u003e. It first describes the two equivalences for conjunction/disjunction.\n$$\n(\\forall x \\phi) \\vee \\psi \\iff \\forall x(\\phi \\vee \\psi) \\tag{1.1}\n$$\u003c/p\u003e\n\u003cp\u003e$$\n(\\exists x \\phi) \\vee \\psi \\iff \\exists x (\\phi \\vee \\psi) \\tag{1.2}\n$$\u003c/p\u003e\n\u003cp\u003eThey show these rules similarly for conjunction. In the next section, they describe the rules for negation:\n$$\n\\neg \\exists x \\phi \\iff \\forall x \\neg \\phi \\tag{2.1}\n$$\u003c/p\u003e\n\u003cp\u003e$$\n\\neg \\forall x \\phi \\iff \\exists x \\neg \\phi \\tag{2.2}\n$$\u003c/p\u003e\n\u003cp\u003eIn the third section, they describe the rules related to implication. With it comes the following quote:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eThese rules can be derived by \u003ca href=\"https://en.wikipedia.org/wiki/Rewriting#Logic\"\u003erewriting\u003c/a\u003e the implication $\\phi \\implies \\psi$ as $\\neg \\phi \\vee \\psi$ and applying the rules for disjunction above.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eThis sounds like \u0026ldquo;we leave this as an exercise to the reader\u0026rdquo;, and a reader I am! Let\u0026rsquo;s label the rule in the quote as $0.1$.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e1.\u003c/strong\u003e Show that $(\\forall x \\phi) \\implies \\psi$ is equivalent to $\\exists x (\\phi \\implies \\psi)$\n$$\n\\begin{align*}\n(\\forall x \\phi) \\implies \\psi \u0026amp;\\iff \\neg (\\forall x \\phi) \\vee \\psi \\tag{0.1} \\\\\n\u0026amp;\\iff (\\exists x \\neg \\phi) \\vee \\psi \\tag{2.2}\\\\\n\u0026amp;\\iff \\exists x (\\neg \\phi \\vee \\psi) \\tag{1.2}\\\\\n\u0026amp;\\iff \\exists x (\\phi \\implies \\psi) \\tag{0.1}\n\\end{align*}\n$$\n\u003cstrong\u003e2.\u003c/strong\u003e Show that $(\\exists x \\phi) \\implies \\psi$ is equivalent to $\\forall x (\\phi \\implies \\psi)$\n$$\n\\begin{align*}\n(\\exists x \\phi) \\implies \\psi \u0026amp;\\iff \\neg(\\exists x \\phi) \\vee \\psi \\tag{0.1}\\\\\n\u0026amp;\\iff \\forall x (\\neg \\phi) \\vee \\psi \\tag{2.1}\\\\\n\u0026amp;\\iff \\forall x (\\neg \\phi \\vee \\psi) \\tag{1.1}\\\\\n\u0026amp;\\iff \\forall x (\\phi \\implies \\psi) \\tag{0.1}\n\\end{align*}\n$$\n\u003cstrong\u003e3.\u003c/strong\u003e Show that $\\phi \\implies (\\exists x \\psi)$ is equivalent to $\\exists x (\\phi \\implies \\psi)$\n$$\n\\begin{align*}\n\\phi \\implies (\\exists x \\psi) \u0026amp;\\iff \\neg \\phi \\vee (\\exists x \\psi) \\tag{0.1}\\\\\n\u0026amp;\\iff (\\exists x \\psi) \\vee \\neg \\phi \\tag{symmetry}\\\\\n\u0026amp;\\iff \\exists x (\\psi \\vee \\neg \\phi) \\tag{1.2}\\\\\n\u0026amp;\\iff \\exists x (\\neg \\phi \\vee \\psi) \\tag{symmetry}\\\\\n\u0026amp;\\iff \\exists x (\\phi \\implies \\psi) \\tag{0.1}\n\\end{align*}\n$$\n\u003cstrong\u003e4.\u003c/strong\u003e Show that $\\phi \\implies (\\forall x \\psi)$ is equivalent to $\\forall x (\\phi \\implies \\psi)$\n$$\n\\begin{align*}\n\\phi \\implies (\\forall x \\psi) \u0026amp;\\iff \\neg \\phi \\vee (\\forall x \\psi) \\tag{0.1}\\\\\n\u0026amp;\\iff \\forall x(\\psi) \\vee \\neg \\phi \\tag{symmetry} \\\\\n\u0026amp;\\iff \\forall x (\\psi \\vee \\neg \\phi) \\tag{1.1}\\\\\n\u0026amp;\\iff \\forall x (\\neg \\phi \\vee \\psi) \\tag{symmetry} \\\\\n\u0026amp;\\iff \\forall x (\\phi \\implies \\psi) \\tag{0.1}\n\\end{align*}\n$$\u003c/p\u003e\n",
    "date_published": "2023.02.17",
    "tags": ["Logic"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/induction-lean3/",
    "url": "https://brandonrozek.com/blog/induction-lean3/",
    "title": "Induction in Lean 3: Three Techniques",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen proving properties of an inductive data type, chances are that we need to perform the induction tactic on the data structure. There are multiple ways to approach this though and each comes with its own pros and cons.\u003c/p\u003e\n\u003cp\u003eIn this post we\u0026rsquo;ll consider the following list structure\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003einductive CustomList (T : Type)\n| cnil : CustomList\n| ccons (hd : T) (tl : CustomList) : CustomList\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eA function that takes a length of the list\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003edef clength {α : Type}:  CustomList α → ℕ \n  | cnil := 0\n  | (ccons b as) := 1 + clength as\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAnd a function that appends two lists\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003edef cappend {α : Type} : CustomList α → CustomList α → CustomList α\n  | cnil         bs := bs\n  | (ccons a as) bs := ccons a (cappend as bs)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow lets say we want to prove the following theorem:\n$$\n\\forall \\mathit{as} \\in \\mathit{CustomList}\u003cdel\u003e\\alpha, \\mathit{cappend}\u003c/del\u003e\\mathit{as}~\\mathit{cnil} = as\n$$\u003c/p\u003e\n\u003cp\u003eThat is, appending \u003ccode\u003ecnil\u003c/code\u003e to a list will produce the same list.\u003c/p\u003e\n\u003ch2 id=\"induction-tactic\"\u003e\u003ccode\u003einduction\u003c/code\u003e tactic\u003c/h2\u003e\n\u003cp\u003eThe first method that is commonly taught is to use the \u003ccode\u003einduction\u003c/code\u003e tactic.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem append_nil1 {α : Type} (as : CustomList α) : cappend as cnil = as := begin\n  induction as,\n\n-- Base Case\n  case CustomList.cnil\n  { \n    show cappend cnil cnil = cnil, from by rewrite [cappend],\n  },\n\n-- Recursive Case\n  case CustomList.ccons : hd tl ih\n  {\n    calc\n      cappend (ccons hd tl) cnil = ccons hd (cappend tl cnil) : by rewrite [cappend]\n      ...                        = ccons hd tl : by rewrite [ih]\n    ,\n  },\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe upside to this approach is that the theorem prover determines for you the subgoals to prove. The issue is that the subgoals aren\u0026rsquo;t explicit within the proof written out. This can harm readability among others. As much as we want to imagine that we\u0026rsquo;re writing code only for computers. We\u0026rsquo;re writing code for other people to read as well.\u003c/p\u003e\n\u003ch2 id=\"pattern-matching\"\u003ePattern Matching\u003c/h2\u003e\n\u003cp\u003eLean has a built in pattern matcher which makes the proof more concise and even reports back to us if we don\u0026rsquo;t consider all the cases.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem append_nil3 {α : Type} : ∀ as : CustomList α, cappend as cnil = as\n| (@cnil α) := show cappend cnil cnil = cnil, from by rewrite [cappend]\n| (ccons hd tl) := show cappend (ccons hd tl) cnil = (ccons hd tl), from by {\n    calc\n      cappend (ccons hd tl) cnil = ccons hd (cappend tl cnil) : by rewrite [cappend]\n      ...                        = ccons hd tl : by rewrite append_nil3\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNote that we had to change the definition header to the $\\forall$ syntax in order for the pattern matching to work. For the base case, we needed to provide the implicit parameter which is why it\u0026rsquo;s stated as \u003ccode\u003e@cnil a\u003c/code\u003e as opposed to only \u003ccode\u003ecnil\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThis is by far the more concise syntax. Mostly readable, however it can be intimidating to parse it initially.\u003c/p\u003e\n\u003ch2 id=\"applying-rec_on\"\u003eApplying \u003ccode\u003erec_on\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eEvery inductive data type in Lean has a built in \u003ccode\u003erec_on\u003c/code\u003e property which allows for recursion to happen. Similarly to applying \u003ccode\u003eor.elim\u003c/code\u003e as opposed to \u003ccode\u003ecases\u003c/code\u003e. We can organize the proof slightly more than the last two approaches.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem append_nil2 {α : Type} (as : CustomList α) : cappend as cnil = as := begin\n  -- Base Case\n  have H_base : cappend cnil cnil = (@cnil α) := by {\n    rewrite [cappend],\n  },\n\n  -- Inductive Step\n  have H_ind : ∀ (hd : α) (tl : CustomList α), cappend tl cnil = tl → cappend (ccons hd tl) cnil = ccons hd tl\n := by {\n    assume hd : α,\n    assume tl : CustomList α,\n    assume H : cappend tl cnil = tl,\n    calc\n      cappend (ccons hd tl) cnil = ccons hd (cappend tl cnil) : by rewrite [cappend]\n      ... = ccons hd tl : by rewrite H,\n  },\n\n  -- Apply induction principle\n  show cappend as cnil = as, from CustomList.rec_on as H_base H_ind,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003ePersonally, this one is the easiest for me to follow. The main issue that I haven\u0026rsquo;t figured out yet is how to cleanly perform double recursion. For example to prove that $\\mathit{clength}\u003cdel\u003e(\\mathit{cappend}\u003c/del\u003e\\mathit{as}\u003cdel\u003e\\mathit{bs}) = \\mathit{clength}\u003c/del\u003e\\mathit{as} + \\mathit{clength}~\\mathit{bs}$, we would need to perform induction on both the $\\mathit{as}$ data structure as well as the $\\mathit{bs}$ datastructure. I haven\u0026rsquo;t quite figured out how to do this appropriately, so if you have suggestions please let me know.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eEach approach comes with its tradeoffs. The \u003ccode\u003erec_on\u003c/code\u003e approach looks clean for single induction proofs. Meanwhile, the pattern matching approach works great with multiple induction. The easiest approach to determining the appropriate subgoals is to use the \u003ccode\u003einduction\u003c/code\u003e tactic. In fact, it sometimes is more efficient to write the entire proof via tactics first and then work towards making it more readable with additional subproofs and rewriting.\u003c/p\u003e\n",
    "date_published": "2023.01.30",
    "tags": ["Formal Methods"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "5b4a36988c6d"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/readable-lean3-proofs/",
    "url": "https://brandonrozek.com/blog/readable-lean3-proofs/",
    "title": "Readable Lean 3 Proofs",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cem\u003eImportant Note: This blog post uses the Lean 3 syntax\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eInteractive theorem provers are notorious for showcasing unreadable proofs. Let\u0026rsquo;s illustrate our point with a couple examples and discuss various ways we can make it more readable.\u003c/p\u003e\n\u003ch2 id=\"disjunction-elimination\"\u003eDisjunction Elimination\u003c/h2\u003e\n\u003cp\u003eDisjunction Elimination or proof by cases is a rule of inference that states the following. Consider you have the following three proofs:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e$p \\vee q$\u003c/li\u003e\n\u003cli\u003e$p \\rightarrow r$\u003c/li\u003e\n\u003cli\u003e$q \\rightarrow r$\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eThen it doesn\u0026rsquo;t matter if it is $p$ rather than $q$ that holds, in the end $r$ holds.  Wikipedia has a \u003ca href=\"https://en.wikipedia.org/wiki/Disjunction_elimination\"\u003enice example\u003c/a\u003e of this concept. I numbered the statements to correspond to the above.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cpre\u003e\u003ccode\u003e1. It is true that either I'm inside or I'm outside.\n2. If I'm inside, I have my wallet on me.\n3. If I'm outside, I have my wallet on me.\n\nTherefore, I have my wallet on me.\n\u003c/code\u003e\u003c/pre\u003e\u003c/blockquote\u003e\n\u003cp\u003eNow let\u0026rsquo;s go over a corresponding proof in Lean.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample (p q r : Prop) : ((p → r) ∧ (q → r) ∧  (p ∨ q)) → r := begin\n  intro H,\n  cases H,\n  cases H_right,\n  cases H_right_right,\n  case or.inl\n  {\n    exact H_left H_right_right,\n  },\n  case or.inr\n  {\n    exact H_right_left H_right_right,\n  },\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe can see a rough example of proof by cases at work. However, it\u0026rsquo;s extremely hard to follow unless we try to emulate the proof state ourselves. What if I say that it doesn\u0026rsquo;t have to be this way? That in fact, we can have proofs that are more readable. First let\u0026rsquo;s start by replacing \u003ccode\u003eintro\u003c/code\u003e with \u003ccode\u003eassume\u003c/code\u003e and add labels to each of our cases.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample (p q r : Prop) : ((p → r) ∧ (q → r) ∧  (p ∨ q)) → r := begin\n  assume H : ((p → r) ∧ (q → r) ∧  (p ∨ q)),\n  cases H with H_pr H_rest,\n  cases H_rest with H_qr H_pq,\n  cases H_pq,\n  case or.inl : H_p\n  {\n    exact H_pr H_p,\n  },\n  case or.inr : H_q\n  {\n    exact H_qr H_q,\n  },\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow we know what \u003ccode\u003eH\u003c/code\u003e is just by looking at the proof. Sadly the cases don\u0026rsquo;t exactly tell us what is contained in each of the hypotheses. But at least they\u0026rsquo;re labeled to give us a clue. We can do better by replacing the initial \u003ccode\u003ecases\u003c/code\u003e statements with \u003ccode\u003ehave\u003c/code\u003e and \u003ccode\u003eexact\u003c/code\u003e with \u003ccode\u003eshow\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample (p q r : Prop) : ((p → r) ∧ (q → r) ∧  (p ∨ q)) → r := begin\n  assume H : ((p → r) ∧ (q → r) ∧  (p ∨ q)),\n  have H_pr : p → r := and.left H,\n  have H_qr : q → r := and.left (and.right H),\n  have H_pq : p ∨ q := and.right (and.right H),\n  cases H_pq,\n  case or.inl : H_p\n  {\n    show r, from H_pr H_p,\n  },\n  case or.inr : H_q\n  {\n    show r, from H_qr H_q,\n  },\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow we have almost a complete picture of what all of our hypotheses are. The only two that are left unspecified are \u003ccode\u003eH_p\u003c/code\u003e and \u003ccode\u003eH_q\u003c/code\u003e. Though we can easily infer what they are by the label. Also by replacing \u003ccode\u003eexact\u003c/code\u003e with \u003ccode\u003eshow\u003c/code\u003e, we have insight on what is trying to be proved at that given point.\u003c/p\u003e\n\u003cp\u003eThe whole beginning of the proof is setting up the problem. Luckily, Lean provides a more concise syntax that better matches how the problem would be presented.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q r : Prop} (H_pr : p → r) (H_qr : q → r) (H_pq : p ∨ q) : r := begin\n  cases H_pq,\n  case or.inl : H_p\n  {\n    show r, from H_pr H_p,\n  },\n  case or.inr : H_q\n  {\n    show r, from H_qr H_q,\n  },\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFinally, if we wanted to condense this more, we can make use of the built in \u003ccode\u003eor.elim\u003c/code\u003e theorem.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q r : Prop} (H_pr : p → r) (H_qr : q → r) (H_pq : p ∨ q) : r := begin\n  show r, from or.elim H_pq H_pr H_qr,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"negation-introduction\"\u003eNegation Introduction\u003c/h2\u003e\n\u003cp\u003eNow let\u0026rsquo;s showcase a more complicated example. Negation introduction or proof by contradiction. Consider that we have the following two proofs:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e$p \\rightarrow q$\u003c/li\u003e\n\u003cli\u003e$p \\rightarrow \\neg q$\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eThen by negation introduction, we have a proof of $\\neg p$.\u003c/p\u003e\n\u003cp\u003eThe proof is going to rely on the law of excluded middle. That is $p \\vee \\neg p$ holds.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eaxiom LEM {p : Prop}: p ∨ ¬ p\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe proof also relies on the following property about negation\n$$\n\\neg p \\iff (p \\rightarrow false)\n$$\nNow let\u0026rsquo;s try to prove negation introduction using the  \u003ccode\u003ecases\u003c/code\u003e tactic.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_pq : p → q) (H_nq : p → ¬q) : ¬p := begin\n  have H_LEM : p ∨ ¬p := LEM,\n  cases H_LEM,\n  case or.inl : H_p\n  {\n    have H_nq : ¬q := H_nq H_p,\n    have H_qf : q → false := H_nq,\n    have H_q : q := H_pq H_p,\n    have H_f : false := H_qf H_q,\n    -- Can prove anything with a falsity\n    -- (Reductio ad absurdum)\n    show ¬p, from false.rec (¬p) H_f,\n  },\n  case or.inr : H_np\n  { \n    show ¬p, from H_np,\n  },\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eSadly this proof is too complicated to condense in one line with \u003ccode\u003eor.elim\u003c/code\u003e. However, we can set it up by creating subproofs with \u003ccode\u003ehave\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003eexample {p q : Prop} (H_pq : p → q) (H_nq : p → ¬q) : ¬p := begin\n  have H_LEM : p ∨ ¬p := LEM,\n  have H_npp : ¬p → ¬p := by {\n    assume H_np : ¬p,\n    show ¬p, from H_np,\n  },\n  have H_pp : p → ¬p := by {\n    assume H_p : p,\n    have H_nq : ¬q := H_nq H_p,\n    have H_qf : q → false := H_nq,\n    have H_q : q := H_pq H_p,\n    have H_f : false := H_qf H_q,\n    show ¬p, from false.rec (¬p) H_f,\n  },\n  show ¬p, from or.elim H_LEM H_pp H_npp,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eWhen most people are taught how to write proofs in an interactive theorem prover, they are shown tactics such as \u003ccode\u003ecases\u003c/code\u003e, \u003ccode\u003esplit\u003c/code\u003e, \u003ccode\u003eleft\u003c/code\u003e, \u003ccode\u003eright\u003c/code\u003e, etc. These tactics manipulate the proof state which is shown to the developer when creating the proof. However, as a line in a proof it provides minimal insight.\u003c/p\u003e\n\u003cp\u003eWe can take the guesswork out of figuring out the proof state by carefully selecting tactics which allow us to explicitely show the object being constructed. In Lean, these are \u003ccode\u003eassume\u003c/code\u003e, \u003ccode\u003ehave\u003c/code\u003e, and \u003ccode\u003eshow\u003c/code\u003e. Also making use of the inference rules themselves such as \u003ccode\u003eor.elim\u003c/code\u003e is a great alternative to \u003ccode\u003ecases\u003c/code\u003e, and \u003ccode\u003eand.intro\u003c/code\u003e is a great alternative to split.\u003c/p\u003e\n\u003cp\u003eIf you have any other techniques on making ITP proofs more readable, please let me know.\u003c/p\u003e\n",
    "date_published": "2023.01.27",
    "tags": ["Formal Methods"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "5fb876d4d819"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/lightweight-gitc-docker-compose/",
    "url": "https://brandonrozek.com/blog/lightweight-gitc-docker-compose/",
    "title": "Deploying a Lightweight Git Server with CGit using Docker-Compose",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn this post, I\u0026rsquo;ll talk about how we can setup CGit within a docker-compose setup. We\u0026rsquo;ll be using the \u003ca href=\"https://hub.docker.com/r/clearlinux/cgit\"\u003eClearLinux CGit\u003c/a\u003e container.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eNote:\u003c/strong\u003e Intel has discountinued the Clear Linux project, and as such the CGit docker container is no longer available. There are other CGit containers on DockerHub, but I have not tested any of them since I (as of Oct 2025) use Forgejo as my Git Forge. Please write in if you\u0026rsquo;ve had success following these instructions with any of the other images out there.\u003c/p\u003e\n\u003ch2 id=\"configuring-apache-webserver\"\u003eConfiguring Apache Webserver\u003c/h2\u003e\n\u003cp\u003eWithin the CGit container, an apache webserver is setup to execute the CGit CGI scripts. This configuration is very similar to the \u003ca href=\"https://github.com/clearlinux/dockerfiles/blob/256680f7c6be8423081e67153de0bff1206f6b63/cgit/httpd-cgit.conf\"\u003edefault one\u003c/a\u003e provided by ClearLinux. However, the default holds your repositories in the \u003ccode\u003e/cgit\u003c/code\u003e subfolder as I wanted it on the root \u003ccode\u003e/\u003c/code\u003e folder.\u003c/p\u003e\n\u003cp\u003e/etc/httpd-cgit.conf\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-apache\" data-lang=\"apache\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eServerName localhost\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Next two lines changed for new document root\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDocumentRoot \u003cspan style=\"color:#e6db74\"\u003e/var/www/cgit\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;Directory\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/var/www/cgit\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    AllowOverride \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Options ExecCGI FollowSymLinks\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Require \u003cspan style=\"color:#66d9ef\"\u003eall\u003c/span\u003e granted\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/Directory\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# cgid module is required to run the cgit binary\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLoadModule cgid_module lib/httpd/modules/mod_cgid.so\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;IfModule\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ecgid_module\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        ScriptSock \u003cspan style=\"color:#e6db74\"\u003e/var/run/cgid.sock\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/IfModule\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Path to cgit stylesheet, graphics\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAlias \u003cspan style=\"color:#e6db74\"\u003e/cgit-data\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/usr/share/cgit\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;Directory\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/usr/share/cgit\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    AllowOverride \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Options \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Require \u003cspan style=\"color:#66d9ef\"\u003eall\u003c/span\u003e granted\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/Directory\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Path to cgit binary\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Next line changed\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eScriptAlias / \u003cspan style=\"color:#e6db74\"\u003e/usr/libexec/cgit/cgi-bin/cgit/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;Directory\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/usr/libexec/cgit/cgi-bin\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    AllowOverride \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Options \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Require \u003cspan style=\"color:#66d9ef\"\u003eall\u003c/span\u003e granted\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/Directory\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"configuring-cgit\"\u003eConfiguring CGit\u003c/h2\u003e\n\u003cp\u003eNow to configure \u003ccode\u003ecgit\u003c/code\u003e itself, we need to create a file called \u003ccode\u003ecgitrc\u003c/code\u003e. Order matters in the declarations, and from what I can gather you should have your \u003ccode\u003escan-path\u003c/code\u003e near the end of the file.\u003c/p\u003e\n\u003cp\u003eTo enable cloning and have it discoverable:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eenable-http-clone\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eclone-prefix\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ehttps://URL/TO/WEBSITE\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo download snapsots of the references\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003esnapshots\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etar.gz zip\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo enable the git config to override the owner and description fields\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eenable-git-config\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eCache up to 1000 output entries\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ecache-size\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRoot Page configuration\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eroot-title\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eBrandon Rozek\u0026#39;s Repositories\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eroot-desc\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003erepository-sort\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eage\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Start all URLs from the root directory\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003evirtual-root\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eServer the appropriate mime-types of certain files\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003emimetype.gif\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eimage/gif\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003emimetype.html\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003etext/html\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003emimetype.jpg\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eimage/jpeg\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003emimetype.jpeg\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eimage/jpeg\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003emimetype.pdf\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eapplication/pdf\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003emimetype.png\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eimage/png\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003emimetype.svg\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eimage/svg+xml\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eStyles for the website\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ecss\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/cgit-data/cgit.css\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003elogo\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/cgit-data/cgit.png\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003efavicon\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/cgit-data/favicon.ico\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003esource-filter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/libexec/cgit/filters/syntax-highlighting.sh\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eabout-filter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/libexec/cgit/filters/about-formatting.sh\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ereadme\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e:README.md\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ereadme\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e:README\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhere to find the repositories\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003escan-path\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/var/www/cgit/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"setting-up-the-container\"\u003eSetting up the container\u003c/h2\u003e\n\u003cp\u003eI prefer using \u003ccode\u003edocker-compose\u003c/code\u003e to help manage all my containers. The first two volumes map the configuration files we created and the last volume holds our repositories.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003ecgit\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eimage\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003edocker.io/clearlinux/cgit\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003econtainer_name\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ecgit\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003ehostname\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ecgit\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003evolumes\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003e/etc/cgitrc:/etc/cgitrc\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003e/etc/httpd-cgit.conf:/etc/httpd/conf.d/httpd-cgit.conf\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003e/var/www/cgit:/var/www/cgit\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003erestart\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ealways\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"populating-it-with-repositories\"\u003ePopulating it with Repositories\u003c/h2\u003e\n\u003cp\u003eWithin \u003ccode\u003e/var/www/cgit\u003c/code\u003e, start cloning your repositories:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit clone --bare REPO_URL\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you enabled gitinfo then for each repository you can run \u003ccode\u003egit config -e\u003c/code\u003e and add the following\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[gitweb]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003eowner\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eName \u0026lt;Email\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\tdescription = Insert your project description here\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"aside-reverse-proxy\"\u003eAside: Reverse Proxy\u003c/h3\u003e\n\u003cp\u003eIn my setup, I have an \u003ccode\u003enginx\u003c/code\u003e container that handles all of the traffic. Therefore, I don\u0026rsquo;t have users enter to the cgit container directly. I handle this by adding the following reverse proxy configuration.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eserver\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e[::]:80\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eserver_name\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eGIT.SERVER.URL\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003elocation\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e301\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ehttps://\u003c/span\u003e$host$request_uri;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eserver\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e443\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003essl\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e[::]:443\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003essl\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ehttp2\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eon\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eserver_name\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eGIT.SERVER.URL\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003essl_certificate\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/path/to/chain\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003essl_certificate_key\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/path/to/private/key\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003einclude\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/etc/letsencrypt/options-ssl-nginx.conf\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003essl_dhparam\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/etc/letsencrypt/ssl-dhparams.pem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elocation\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_pass\u003c/span\u003e  \u003cspan style=\"color:#e6db74\"\u003ehttp://cgit\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eHost\u003c/span\u003e $host;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eX-Real-IP\u003c/span\u003e $remote_addr;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eX-Forwarded-For\u003c/span\u003e $proxy_add_x_forwarded_for;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eX-Forwarded-Host\u003c/span\u003e $server_name;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e       \t\u003cspan style=\"color:#75715e\"\u003e# Needed to get around HTTP2 Streaming Errors\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_hide_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eUpgrade\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eAfter all the configuration, you should be able to pull it up using \u003ccode\u003edocker-compose\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker-compose up cgit\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"references\"\u003eReferences\u003c/h2\u003e\n\u003cp\u003eThese references talked about setting up cgit outside of docker, but they helped me understand the various configuration files needed.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://russellhaering.com/running-cgit-under-nginx/\"\u003ehttps://russellhaering.com/running-cgit-under-nginx/\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://jakesthoughts.xyz/blog/setting-up-cgit.html\"\u003ehttps://jakesthoughts.xyz/blog/setting-up-cgit.html\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://www.yaroslavps.com/weblog/minimal-git-server/\"\u003ehttps://www.yaroslavps.com/weblog/minimal-git-server/\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://blog.stefan-koch.name/2020/02/16/installing-cgit-nginx-on-debian\"\u003ehttps://blog.stefan-koch.name/2020/02/16/installing-cgit-nginx-on-debian\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://bryanbrattlof.com/cgit-nginx-gitolite-a-personal-git-server/\"\u003ehttps://bryanbrattlof.com/cgit-nginx-gitolite-a-personal-git-server/\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://matejamaric.com/blog/git-server/\"\u003ehttps://matejamaric.com/blog/git-server/\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n",
    "date_published": "2023.01.20",
    "tags": ["Git"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "b86160f97df9"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/bmaptool-iso/",
    "url": "https://brandonrozek.com/blog/bmaptool-iso/",
    "title": "Bmaptool: A simpler way to copy ISOs",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eBmaptool is a project created by Intel for creating and copying data using block maps. It\u0026rsquo;s meant to be a simpler, faster, and more reliable tool than \u003ccode\u003edd\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFrom their \u003ca href=\"https://github.com/intel/bmap-tools\"\u003eGitHub page\u003c/a\u003e:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cul\u003e\n\u003cli\u003eFaster. Depending on various factors, like write speed, image size, how full is the image, and so on, \u003ccode\u003ebmaptool\u003c/code\u003e was 5-7 times faster than \u003ccode\u003edd\u003c/code\u003e in the Tizen IVI project.\u003c/li\u003e\n\u003cli\u003eIntegrity. \u003ccode\u003ebmaptool\u003c/code\u003e verifies data integrity while flashing, which means that possible data corruptions will be noticed immediately.\u003c/li\u003e\n\u003cli\u003eUsability. \u003ccode\u003ebmaptool\u003c/code\u003e can read images directly from the remote server, so users do not have to download images and save them locally.\u003c/li\u003e\n\u003cli\u003eProtects user\u0026rsquo;s data. Unlike \u003ccode\u003edd\u003c/code\u003e, if you make a mistake and specify a wrong block device name, \u003ccode\u003ebmaptool\u003c/code\u003e will less likely destroy your data because it has protection mechanisms which, for example, prevent \u003ccode\u003ebmaptool\u003c/code\u003e from writing to a mounted block device.\u003c/li\u003e\n\u003c/ul\u003e\u003c/blockquote\u003e\n\u003cp\u003eIt comes with two commands \u003cstrong\u003ecreate\u003c/strong\u003e and \u003cstrong\u003ecopy\u003c/strong\u003e. Create generates the block maps which isn\u0026rsquo;t required to use the application. However, having a bmap will speed up the copying process. The syntax of the copy command is the following\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo bmaptool copy SRC DST\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLet\u0026rsquo;s say that I want to flash the latest Ubuntu ISO to a USB stick located at \u003ccode\u003e/dev/sdX\u003c/code\u003e. As the third bullet point claims, I can easily use the URL as SRC and since we don\u0026rsquo;t have a bmap, we\u0026rsquo;ll have to specify that with the flag \u003ccode\u003e--nobmap\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo bmaptool copy --nobmap https://releases.ubuntu.com/22.04.1/ubuntu-22.04.1-desktop-amd64.iso /dev/sdX\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eExample run on my desktop:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ebmaptool: info: no bmap given, copy entire image to \u0026#39;/dev/sdX\u0026#39;\n/\nbmaptool: info: synchronizing \u0026#39;/dev/sdX\u0026#39;\nbmaptool: info: copying time: 3m 7.3s, copying speed 19.5 MiB/sec\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow if we have the ISO downloaded on our computer, we can take the time to create a bmap for it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo bmaptool create ubuntu-22.04.1-desktop-amd64.iso \u0026gt; ubuntu-22.04.1-desktop-amd64.bmap\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eA bmap file is a human readable XML file that shows the block map and the checksums for each block.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u0026lt;?xml version=\u0026#34;1.0\u0026#34; ?\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u0026lt;!-- This file contains the block map for an image file, which is basically\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e     a list of useful (mapped) block numbers in the image file. In other words,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e     it lists only those blocks which contain data (boot sector, partition\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e     table, file-system metadata, files, directories, extents, etc). These\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e     blocks have to be copied to the target device. The other blocks do not\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e     contain any useful data and do not have to be copied to the target\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e     device.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e     The block map an optimization which allows to copy or flash the image to\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e     the image quicker than copying of flashing the entire image. This is\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e     because with bmap less data is copied: \u0026lt;MappedBlocksCount\u0026gt; blocks instead\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e     of \u0026lt;BlocksCount\u0026gt; blocks.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e     Besides the machine-readable data, this file contains useful commentaries\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e     which contain human-readable information like image size, percentage of\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e     mapped data, etc.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e     The \u0026#39;version\u0026#39; attribute is the block map file format version in the\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e     \u0026#39;major.minor\u0026#39; format. The version major number is increased whenever an\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e     incompatible block map format change is made. The minor number changes\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e     in case of minor backward-compatible changes. --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;bmap\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eversion=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2.0\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e\u0026lt;!-- Image size in bytes: 3.6 GiB --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;ImageSize\u0026gt;\u003c/span\u003e 3826831360 \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/ImageSize\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e\u0026lt;!-- Size of a block in bytes --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;BlockSize\u0026gt;\u003c/span\u003e 4096 \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/BlockSize\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e\u0026lt;!-- Count of blocks in the image file --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;BlocksCount\u0026gt;\u003c/span\u003e 934285 \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/BlocksCount\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e\u0026lt;!-- Count of mapped blocks: 3.6 GiB or 100.0%   --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;MappedBlocksCount\u0026gt;\u003c/span\u003e 934285 \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/MappedBlocksCount\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e\u0026lt;!-- Type of checksum used in this file --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;ChecksumType\u0026gt;\u003c/span\u003e sha256 \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/ChecksumType\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e\u0026lt;!-- The checksum of this bmap file. When it is calculated, the value of\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e         the checksum has be zero (all ASCII \u0026#34;0\u0026#34; symbols).  --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;BmapFileChecksum\u0026gt;\u003c/span\u003e e69f56b4cf11a26fba8700bc66a443a20f667d0d0fe1c2d8028715ac060c402d \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/BmapFileChecksum\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e\u0026lt;!-- The block map which consists of elements which may either be a\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e         range of blocks or a single block. The \u0026#39;chksum\u0026#39; attribute\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e         (if present) is the checksum of this blocks range. --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;BlockMap\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;Range\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003echksum=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;c396e956a9f52c418397867d1ea5c0cf1a99a49dcf648b086d2fb762330cc88d\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e 0-934284 \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/Range\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/BlockMap\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/bmap\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOnce we have generated the bmap, we can copy the ISO to the device.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo bmaptool copy ubuntu-22.04.1-desktop-amd64.iso /dev/sdX\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eExample run on my desktop:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ebmaptool: info: discovered bmap file \u0026#39;ubuntu-22.04.1-desktop-amd64.bmap\u0026#39;\nbmaptool: info: block map format version 2.0\nbmaptool: info: 934285 blocks of size 4096 (3.6 GiB), mapped 934285 blocks (3.6 GiB or 100.0%)\nbmaptool: info: copying image \u0026#39;ubuntu-22.04.1-desktop-amd64.iso\u0026#39; to block device \u0026#39;/dev/sdX\u0026#39; using bmap file \u0026#39;ubuntu-22.04.1-desktop-amd64.bmap\u0026#39;\nbmaptool: info: 100% copied\nbmaptool: info: synchronizing \u0026#39;/dev/sdX\u0026#39;\nbmaptool: info: copying time: 2m 49.2s, copying speed 21.6 MiB/sec\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eRecall that the \u003ccode\u003ebmap\u003c/code\u003e generation isn\u0026rsquo;t necessary, as you can pass in the \u003ccode\u003e--nobmap\u003c/code\u003e flag.\u003c/p\u003e\n",
    "date_published": "2023.01.18",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "b4f33babc3ac"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/decentralized-pgp-keys-wkd/",
    "url": "https://brandonrozek.com/blog/decentralized-pgp-keys-wkd/",
    "title": "Decentralized PGP Keys with WKD",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eAfter creating a PGP key, it is common to distribute it to various keyservers. However, anyone can upload to these keyservers impersonating someone else. One solution is to use a decentralized identities approach, however, if your email is on your own domain that you tell every soul about, why not have your own website host the key? This is where the Web Key Directory (WKD) protocol comes in.\u003c/p\u003e\n\u003ch2 id=\"setting-up-wkd\"\u003eSetting up WKD\u003c/h2\u003e\n\u003cp\u003eTo start we need to create a new folder on our webserver:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir .well-known/openpgpkey/hu\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn it, add an empty policy file\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-bsah\" data-lang=\"bsah\"\u003etouch .well-known/openpgpkey/hu/policy\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow we need to add our key to the folder. The key needs to be stored in the file named after the email\u0026rsquo;s WKD hash. We can get this hash through the following command:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egpg --with-wkd-hash --fingerprint brozek@brandonrozek.com\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReplacing my email with yours. At the current moment, this returns the following for me:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epub   ed25519 2022-12-14 [SC] [expires: 2023-12-14]\n      5F37 830B FA46 FF78 81F4  7AC7 8DF7 9C3D C5FC 658A\nuid           [ultimate] Brandon Rozek \u0026lt;brozek@brandonrozek.com\u0026gt;\n              o1dbwkdx683fduwgzmrbwa3yip41frdn@brandonrozek.com\nuid           [ultimate] Brandon Rozek \u0026lt;hello@brandonrozek.com\u0026gt;\n              im4cc8qhazwkfsi65a8us1bc5gzk1o4p@brandonrozek.com\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe string starting with\u003ccode\u003eo1dbwk\u003c/code\u003e is the WKD hash for \u003ccode\u003ebrozek@brandonrozek.com\u003c/code\u003e and the string starting with \u003ccode\u003eim4cc8qh\u003c/code\u003e is the WKD hash for \u003ccode\u003ehello@brandonrozek.com\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s store that hash in \u003ccode\u003e$WKD\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport WKD\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;o1dbwkdx683fduwgzmrbwa3yip41frdn\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe WKD specification says to upload the non-armored (binary) version of our key.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egpg --no-armor --export brozek@brandonrozek.com \u0026gt; .well-known/openpgpkey/hu/$WKD\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAfter uploading it to our webserver, it needs to be configured with the right content type and access control headers.\u003c/p\u003e\n\u003cp\u003eIn Nginx:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003elocation\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/.well-known/openpgpkey\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003edefault_type\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eapplication/octet-stream\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eadd_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eAccess-Control-Allow-Origin\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;*\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow we can check our setup using the website:\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://WebKeyDirectory.com\"\u003ehttps://WebKeyDirectory.com\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"using-wkd\"\u003eUsing WKD\u003c/h2\u003e\n\u003cp\u003eMany applications currently support WKD, though I\u0026rsquo;ll show how we can use \u003ccode\u003egpg\u003c/code\u003e to search for someone\u0026rsquo;s key.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egpg --auto-key-locate wkd --locate-key brozek@brandonrozek.com\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will not only locate but import the key into our keystore.\u003c/p\u003e\n\u003cp\u003eWith WKD, we didn\u0026rsquo;t have to trust anyone but the DNS provider in order to retrieve the key. The biggest downside with this approach, however, is that most people do not have an email on their own domain. Since nowadays, many people use gmail as their primary provider, they will have to fallback to using a different approach for distributing their keys.\u003c/p\u003e\n\u003ch2 id=\"references\"\u003eReferences\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://shibumi.dev/posts/how-to-setup-your-own-wkd-server/\"\u003ehttps://shibumi.dev/posts/how-to-setup-your-own-wkd-server/\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://www.sindastra.de/p/1905/how-to-set-up-pgp-wkd-web-key-directory\"\u003ehttps://www.sindastra.de/p/1905/how-to-set-up-pgp-wkd-web-key-directory\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://shivering-isles.com/Lets-discover-OpenPGP-keys\"\u003ehttps://shivering-isles.com/Lets-discover-OpenPGP-keys\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://wiki.gnupg.org/WKD\"\u003ehttps://wiki.gnupg.org/WKD\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n",
    "date_published": "2023.01.04",
    "tags": ["GPG/PGP"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "7b6197f860fc"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/decentralized-identity-pgp-keyoxide/",
    "url": "https://brandonrozek.com/blog/decentralized-identity-pgp-keyoxide/",
    "title": "Decentralized Identities with PGP Annotations and Keyoxide",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eUnder asymmetric encryption, for you to send me a message that only I can read you would need to encrypt the message with my public key. I then would have a corresponding private key that can decrypt the message. Public keys are then usually stored onto keyservers for others to query. When querying for a key, how do you know that the public key actually belongs to me? It turns out, you can\u0026rsquo;t since anyone can upload a key pretending to be me.\u003c/p\u003e\n\u003cp\u003eWhat\u0026rsquo;s the solution? When PGP first came around, it was built around the \u003ca href=\"https://en.wikipedia.org/wiki/Web_of_trust\"\u003eWeb of Trust\u003c/a\u003e concept. The idea is that people would go to key-signing parties and verify in person that they are who they say they are. From a graph would be built out showing who verified who. Sadly this idea didn\u0026rsquo;t take off. A very small segment of the population attends key signing parties.\u003c/p\u003e\n\u003ch2 id=\"keybase\"\u003eKeybase\u003c/h2\u003e\n\u003cp\u003eIn 2014, \u003ca href=\"https://keybase.io/\"\u003eKeybase\u003c/a\u003e was created to help solve this issue. The concept behind it is that we have other identities in the web, and by associating a keybase profiles to these identities others can have a strong confidence on who they are speaking to.\u003c/p\u003e\n\u003cp\u003eFor example, I own the website brandonrozek.com and am known as \u003ca href=\"https://fosstodon.org/@brozek\"\u003e@brozek@fosstodon.org\u003c/a\u003e on Mastodon. On those platforms, I would create a post using the private key on keybase which claims that I own the user profile on keybase. Similarly on keybase I would link to my website and mastodon profile to say that I claim those.\u003c/p\u003e\n\u003cp\u003eFor a while, this was working great. Then in 2019 the following article comes out of their blog:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eKeybase + Stellar is live for everyone!\u003c/p\u003e\n\u003cp\u003eSource: \u003ca href=\"https://keybase.io/blog/keybase-stellar-launch\"\u003ehttps://keybase.io/blog/keybase-stellar-launch\u003c/a\u003e\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eThe promotion of cryptocurrency makes you wonder how they are doing financially. Then in 2020, we see:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eKeybase joins Zoom\u003c/p\u003e\n\u003cp\u003eSource: \u003ca href=\"https://keybase.io/blog/keybase-joins-zoom\"\u003ehttps://keybase.io/blog/keybase-joins-zoom\u003c/a\u003e\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eUpon further reflection, several questions arise:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eWhy am I depending on Keybase to show which users I\u0026rsquo;m connected to? Ideally, this should be decentralized.\u003c/li\u003e\n\u003cli\u003eKeybase holds access to the private key. While this makes the user experience easier since I don\u0026rsquo;t need to worry about those details. We should be encouraging people to hold their private keys instead.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWhat\u0026rsquo;s a great alternative to Keybase then? This is where \u003ca href=\"https://keyoxide.org/\"\u003eKeyoxide\u003c/a\u003e comes in.\u003c/p\u003e\n\u003ch2 id=\"keyoxide--pgp-notations\"\u003eKeyoxide \u0026amp; PGP Notations\u003c/h2\u003e\n\u003cp\u003eYarmo Mackenbach wanted to create a project that\u0026rsquo;s decentralized in nature. This means that Keyoxide doesn\u0026rsquo;t hold the keys. Instead it depends on either:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eWeb Key Directory (WKD) protocol where the keys are stored on your own server belonging to the domain.\u003c/li\u003e\n\u003cli\u003eHTTP Keyserver Protocol (HKP) where Keyoxide queries keys.openpgp.org\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWithin the key you upload, you can add a PGP notation. This allows us to provide additional text on what accounts we own.\u003c/p\u003e\n\u003cp\u003eFor example the notation of:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eproof@ariadne.id=dns:brandonrozek.com?type=TXT\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eclaims that I own the domain \u003ccode\u003ebrandonrozek.com\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eTo provide the necessary backlink, the \u003ca href=\"https://docs.keyoxide.org/service-providers/dns/\"\u003eKeyoxide documentation\u003c/a\u003e says to create a TXT record with my PGP fingerprint.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eopenpgp4fpr:5F37830BFA46FF7881F47AC78DF79C3DC5FC658A\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNotice how nowhere in the process do we reference Keyoxide or their servers. This only depends upon the keys that I upload onto the Internet and the appropriate backlinks. Keyoxide in this case, only serves as a validator, making sure that the links exist.\u003c/p\u003e\n\u003cp\u003eMy Keyoxide profile: \u003ca href=\"https://keyoxide.org/wkd/brozek%40brandonrozek.com\"\u003ehttps://keyoxide.org/wkd/brozek%40brandonrozek.com\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eIn fact, Keyoxide is \u003ca href=\"https://codeberg.org/keyoxide/\"\u003eopen source\u003c/a\u003e meaning that anyone can host their own instance to perform the validation checks.\u003c/p\u003e\n",
    "date_published": "2023.01.04",
    "tags": ["GPG/PGP"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "5703b335e3a8"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/obtaining-multiple-solutions-z3/",
    "url": "https://brandonrozek.com/blog/obtaining-multiple-solutions-z3/",
    "title": "Obtaining Multiple Solutions Z3",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003ePlaying around with Diophantine solvers, I wanted to obtain the solutions of the following equation:\n$$\n5a + 4b - 3c = 0\n$$\nLet\u0026rsquo;s encode that using Z3\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e z3 \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Encode Equation\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ea, b, c \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Ints(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;a, b, c\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003es \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Solver()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003es\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd(\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e a \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e b \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e c \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Find solution\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eresult \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e s\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emodel()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e result \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e sat:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(result)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis code snippet returns\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e[a = 0, b = 0, c = 0]\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow there are multiple solutions to this Diophantine equation, so how do we get the others? It turns out after searching around StackOverflow (see references) the only way is to add the previous solutions as constraints.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# This encodes the last solution as a constraint\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eblock \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e var \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e m:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    block\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend(var() \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e m[var])\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003es\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd(Or(block))\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFormulaically, this corresponds to:\n$$\na \\ne 0 \\vee b \\ne 0 \\vee c \\ne 0\n$$\nIf you look at the references, it\u0026rsquo;s hard to encode these constraints generally. This is because Z3 is a powerful SMT solver working with many different theories.  Though if we restrict ourselves to the Diophantine equations, we can write a function that acts as a generator for all of the solutions:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e z3\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eget_solutions\u003c/span\u003e(s: z3\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ez3\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eSolver):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    result \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e s\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003echeck()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# While we still get solutions\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e (result \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e z3\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esat):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      m \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e s\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emodel()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#66d9ef\"\u003eyield\u003c/span\u003e m\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#75715e\"\u003e# Add new solution as a constraint\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      block \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e var \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e m:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          block\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend(var() \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e m[var])\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      s\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd(z3\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eOr(block))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#75715e\"\u003e# Look for new solution\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      result \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e s\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003echeck()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow for our example, this allows us to do the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e z3 \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ea, b, c \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Ints(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;a, b, c\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003es \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Solver()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003es\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd(\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e a \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e b \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e c \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esolutions \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e get_solutions(s)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eupper_bound \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e solution, _ \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e zip(solutions, range(upper_bound)):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(solution)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe solutions of a linear Diophantine equation can be easily parameterized so I don\u0026rsquo;t recommend using Z3 in this way. Though I think this exercise is informative for other theories you might be trying to satisfy.\u003c/p\u003e\n\u003ch2 id=\"references\"\u003eReferences\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://stackoverflow.com/questions/11867611/z3py-checking-all-solutions-for-equation/70656700#70656700\"\u003ehttps://stackoverflow.com/questions/11867611/z3py-checking-all-solutions-for-equation/70656700#70656700\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://stackoverflow.com/questions/11867611/z3py-checking-all-solutions-for-equation/70656700#70656700\"\u003ehttps://stackoverflow.com/questions/11867611/z3py-checking-all-solutions-for-equation/70656700#70656700\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://stackoverflow.com/questions/63231398/trying-to-find-all-solutions-to-a-boolean-formula-using-z3-in-python/63232953#63232953\"\u003ehttps://stackoverflow.com/questions/63231398/trying-to-find-all-solutions-to-a-boolean-formula-using-z3-in-python/63232953#63232953\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n",
    "date_published": "2022.12.31",
    "tags": ["Formal Methods"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "d4f11c04d852"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/mastodon-webfinger-alias-using-redirects/",
    "url": "https://brandonrozek.com/blog/mastodon-webfinger-alias-using-redirects/",
    "title": "Mastodon/Webfinger Alias using HTTP Redirects",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eMastodon uses the Webfinger protocol to find users. For example, if you search for \u003ccode\u003e@brozek@brandonrozek.com\u003c/code\u003e it will access the url:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ehttps://brandonrozek.com/.well-known/webfinger?resource=acct:brozek@brandonrozek.com\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAs of the time of writing, I do not have a strong interest in running my own Mastodon server. I instead am active on \u003ccode\u003e@brozek@fosstodon.org\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ehttps://fosstodon.org/.well-known/webfinger?resource=acct:brozek@fosstodon.org\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIf you try to access the last URL in your web browser, you\u0026rsquo;ll notice that it doesn\u0026rsquo;t return anything. This is because the server will only respond if we specify the content type. In \u003ccode\u003ecurl\u003c/code\u003e, we can specify that through the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl -H \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Accept: application/jrd+json\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\thttps://fosstodon.org/.well-known/webfinger?resource\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eacct:brozek@fosstodon.org\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eCurrently, it returns the following\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;subject\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;acct:brozek@fosstodon.org\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;aliases\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://fosstodon.org/@brozek\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://fosstodon.org/users/brozek\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;links\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;rel\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;http://webfinger.net/rel/profile-page\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;text/html\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;href\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://fosstodon.org/@brozek\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;rel\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;self\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;application/activity+json\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;href\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://fosstodon.org/users/brozek\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;rel\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;http://ostatus.org/schema/1.0/subscribe\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;template\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://fosstodon.org/authorize_interaction?uri={uri}\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSome people have been copy pasting the contents of this file, onto their webserver. However, this makes it so that we can\u0026rsquo;t have multiple aliases on this domain. Instead, we can use our webserver (mine is \u003ccode\u003enginx\u003c/code\u003e) to redirect the user when the specified handle is requested.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003elocation\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/.well-known/webfinger\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003edefault_type\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eapplication/jrd+json\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eadd_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eAccess-Control-Allow-Origin\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;*\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e(\u003c/span\u003e$arg_resource = \u003cspan style=\"color:#e6db74\"\u003eacct:brozek@brandonrozek.com)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e301\u003c/span\u003e $scheme://fosstodon.org/.well-known/webfinger?resource=acct:brozek@fosstodon.org;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow this isn\u0026rsquo;t a perfect alias, you can\u0026rsquo;t reference \u003ccode\u003e@brozek@brandonrozek.com\u003c/code\u003e and have me be notified. Instead, this only works in user discovery.\u003c/p\u003e\n",
    "date_published": "2022.12.31",
    "tags": ["Mastodon"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "dd0c680d595e"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/personal-simple-web-archive/",
    "url": "https://brandonrozek.com/blog/personal-simple-web-archive/",
    "title": "Personal Web Archive and How I Archive Single Web pages",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe \u003ca href=\"https://web.archive.org/\"\u003eInternet Archive\u003c/a\u003e is great for providing a centralized database of snapshots of websites throughout time. What happens though when you want to have\nyour own offline copy during times of little to no internet\naccess? \u003ca href=\"https://archivebox.io/\"\u003eArchivebox\u003c/a\u003e is one solution\nto such problem. It behaves similarly to the Internet\narchive and also allows importing of RSS feeds to save local copies of blog posts. To install it, you can use \u003ccode\u003epipx\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epipx install archivebox\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor the rest of this post, however, I want to talk about a simpler tool. A combination of \u003ccode\u003ewget\u003c/code\u003e and \u003ccode\u003epython -m http.server\u003c/code\u003e. In the past, I\u0026rsquo;ve used \u003ccode\u003ewget\u003c/code\u003e to \u003ca href=\"/blog/archivingsites/\"\u003emirror entire websites\u003c/a\u003e. We can adjust the command slightly so that it doesn\u0026rsquo;t follow links and instead only looks at a single webpage.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewget --convert-links \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --adjust-extension \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --no-clobber \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --page-requisites \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     INSERT_URL_HERE\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow assume we have a folder full of downloaded websites. To view them, we can use any HTTP server. One of the easiest to temporarily setup currently is Python\u0026rsquo;s built in one.\u003c/p\u003e\n\u003cp\u003eTo serve all the files in the current directory\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epython -m http.server OPTIONAL_PORT_NUM\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you leave the port number field empty, then this returns\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eServing HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eA few nice side-effects of using \u003ccode\u003ewget\u003c/code\u003e and \u003ccode\u003epython\u003c/code\u003e.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003ePython\u0026rsquo;s default webserver shows a list of files in the directory. This can make it easier to browse around the web archive.\u003c/li\u003e\n\u003cli\u003eThe \u003ccode\u003ewget\u003c/code\u003e flags make it so that if you want to archive \u003ccode\u003ehttps://brandonrozek.com/blog/personal-simple-web-archive/\u003c/code\u003e then all you need to access is \u003ccode\u003ehttp://localhost:8000/brandonrozek.com/blog/personal-simple-web-archive\u003c/code\u003e. In other words, it preserves URLs.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eNow this approach isn\u0026rsquo;t perfect, if a webpage makes heavy use of javascript or server side features then it\u0026rsquo;ll be incomplete. Though for the majority of the wiki pages or blog posts I want to save for future reference, this approach works well.\u003c/p\u003e\n\u003cp\u003eMy full script is below:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/sh\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o errexit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o nounset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o pipefail\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eARCHIVE_FOLDER\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$HOME\u003cspan style=\"color:#e6db74\"\u003e/webarchive\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eshow_usage\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Usage: archivesite [URL]\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check argument count\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$#\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -ne \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    show_usage\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$ARCHIVE_FOLDER\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewget --convert-links \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --adjust-extension \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --page-requisites \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --no-verbose \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Keep track of requested URLs\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u0026gt;\u0026gt; saved_urls.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.12.24",
    "tags": ["Archive"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "9b2818a6e154"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/concatenating-pdf-linux/",
    "url": "https://brandonrozek.com/blog/concatenating-pdf-linux/",
    "title": "Concatenating PDF files in Linux",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eEvery so often I need to combine several images into a single PDF. First, to convert an image to a PDF we can use \u003ccode\u003eimagemagick\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econvert -quality \u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e Image.png Scanned.pdf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo combine or concatenate multiple PDF files, we can use \u003ccode\u003eghostscript\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egs -sDEVICE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003epdfwrite \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e   -sOUTPUTFILE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eoutput.pdf \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e   -dNOPAUSE \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e   -dBATCH \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e   input0.pdf input1.pdf input2.pdf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eFlag\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-sDEVICE\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eDevice used for processing the output file type. Use \u003ccode\u003epdfwrite\u003c/code\u003e to write to a PDF file.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-sOUTPUTFILE\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003ePath to save the resulting file output.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-dNOPAUSE\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eDisables the prompting and pausing at the end of each page.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-dBATCH\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eFinishes interpreting after processing the inputted files\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eAlternatively you can use \u003ccode\u003epdftk\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epdftk input0.pdf input1.pdf input2.pdf \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\tcat output output.pdf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLastly, you can also use \u003ccode\u003eimagemagick\u003c/code\u003e. Do note, however, that this program often leads to larger file sizes.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econvert input0.pdf input1.pdf input2.pdf output.pdf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"aside-pixel-densities\"\u003eAside: Pixel Densities\u003c/h2\u003e\n\u003cp\u003eOne issue I came across is that the pages were of different sizes. This is often because the pages can be of different pixel densities.\u003c/p\u003e\n\u003cp\u003eTo check run \u003ccode\u003epdfimages\u003c/code\u003e and look at the 3rd to last and 2nd to last columns:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epdfimages -list filename.pdf \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epage   num  type   width height color comp bpc  enc interp  object ID x-ppi y-ppi size ratio\n--------------------------------------------------------------------------------------------\n   1     0 image     613    77  rgb     3   8  jpeg   no         8  0  1071  1076 9456B 6.7%\n   1     1 image    2692  3496  rgb     3   8  jpeg   no         9  0   329   329  418K 1.5%\n   2     2 image     613    77  rgb     3   8  jpeg   no         8  0   915   919 9456B 6.7%\n   2     3 image    2300  3016  rgb     3   8  jpeg   no        15  0   282   282  322K 1.6%\n   3     4 image     613    77  rgb     3   8  jpeg   no         8  0   937   942 9456B 6.7%\n   3     5 image    2356  3024  rgb     3   8  jpeg   no        21  0   288   288  150K 0.7%\n   4     6 image    1686  2200  rgb     3   8  jpeg   no        27  0   204   204  622K 5.7%\n   5     7 image    5100  7016  rgb     3   8  jpeg   no        33  0   600   600 1193K 1.1%\n   6     8 image     613    77  rgb     3   8  jpeg   no         8  0  1104  1110 9456B 6.7%\n   6     9 image    2776  3720  rgb     3   8  jpeg   no        38  0   339   339  231K 0.8%\n   7    10 image     613    77  rgb     3   8  jpeg   no         8  0   939   943 9456B 6.7%\n   7    11 image    2360  3072  rgb     3   8  jpeg   no        44  0   289   289  151K 0.7%\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe can then use \u003ccode\u003eimagemagick\u003c/code\u003e to enforce a certain pixel density. The tradeoff being that the file size might increase.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econvert -density \u003cspan style=\"color:#ae81ff\"\u003e300\u003c/span\u003e input.pdf output.pdf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you happen to know a different way to enforce a pixel density that doesn\u0026rsquo;t have a file size increase tradeoff. Please get in touch.\u003c/p\u003e\n",
    "date_published": "2022.12.18",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "21f4d18fcb56"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/blogroll-from-subscriptions/",
    "url": "https://brandonrozek.com/blog/blogroll-from-subscriptions/",
    "title": "Blogroll From Subscriptions",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhile I was browsing around personal websites, I found a fun little piece of code from Jake Bauer\u0026rsquo;s \u003ca href=\"https://www.paritybit.ca/links\"\u003elinks page\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egrep \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;xmlUrl\u0026#34;\u003c/span\u003e static/subscriptions.opml |\u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003esed \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;s/.*text=\\\u0026#34;\\(.*\\)\\\u0026#34; xmlUrl=\\\u0026#34;\\(https\\?:\\/\\/[^\\/]*\\/\\)\\(.*\\)\\\u0026#34; .*/\u0026lt;li\u0026gt;\u0026lt;a href=\\\u0026#34;\\2\\\u0026#34;\u0026gt;\\1\u0026lt;\\/a\u0026gt; (\u0026lt;a href=\\\u0026#34;\\2\\3\\\u0026#34;\u0026gt;feed\u0026lt;\\/a\u0026gt;)\u0026lt;\\/li\u0026gt;/g\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis takes the subscriptions exported from \u003ca href=\"https://github.com/nkanaev/yarr\"\u003eyarr\u003c/a\u003e and generates a HTML list which you can then include in a blogroll page.\u003c/p\u003e\n\u003cp\u003eRunning this script on my export from \u003ca href=\"https://feedbin.com/\"\u003eFeedbin\u003c/a\u003e yielded some extra metadata being shown in the HTML.  For example: \u003ccode\u003eJoke Bauer type=\u0026quot;rss\u0026quot; (Feed)\u003c/code\u003e. So let\u0026rsquo;s edit the code snippet above so that it works for my subscription export.\u003c/p\u003e\n\u003cp\u003eFrom my \u003ccode\u003esubscriptions.xml\u003c/code\u003e here\u0026rsquo;s an example entry:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;outline\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etext=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Jake Bauer\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etitle=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Jake Bauer\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etype=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;rss\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003exmlUrl=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://www.paritybit.ca/feed.xml\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehtmlUrl=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://www.paritybit.ca/\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIt looks like I need to extract the \u003ccode\u003etitle\u003c/code\u003e, \u003ccode\u003exmlUrl\u003c/code\u003e, and \u003ccode\u003ehtmlUrl\u003c/code\u003e attributes in that specific order. I\u0026rsquo;ll use the same technique from a previous post on \u003ca href=\"/blog/capturing-quoted-string-sed\"\u003ecapturing quoated strings\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egrep \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;xmlUrl\u0026#34;\u003c/span\u003e subscriptions.xml |\u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003esed \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;s/.*title=\\\u0026#34;\\([^\\\u0026#34;]*\\)\\\u0026#34;.*xmlUrl=\\\u0026#34;\\([^\\\u0026#34;]*\\)\\\u0026#34;.*htmlUrl=\\\u0026#34;\\([^\\\u0026#34;]*\\)\\\u0026#34;.*/\u0026lt;li\u0026gt;\u0026lt;a href=\\\u0026#34;\\3\\\u0026#34;\u0026gt;\\1\u0026lt;\\/a\u0026gt; (\u0026lt;a href=\\\u0026#34;\\2\\\u0026#34;\u0026gt;feed\u0026lt;\\/a\u0026gt;)\u0026lt;\\/li\u0026gt;/g\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can then clean this up into a \u003ccode\u003ecreate_blogroll\u003c/code\u003e script saved within \u003ca href=\"/blog/customexec/\"\u003e\u003ccode\u003e~/.local/bin\u003c/code\u003e\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/sh\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o errexit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o nounset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eshow_usage\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Usage: create_blogroll [subscriptions.xml]\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check argument count\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$#\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -ne \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    show_usage\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eQUOTED_STR\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\\\u0026#34;\\([^\\\u0026#34;]*\\)\\\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eXML_EXPR\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;.*title=\u003c/span\u003e$QUOTED_STR\u003cspan style=\"color:#e6db74\"\u003e.*xmlUrl=\u003c/span\u003e$QUOTED_STR\u003cspan style=\"color:#e6db74\"\u003e.*htmlUrl=\u003c/span\u003e$QUOTED_STR\u003cspan style=\"color:#e6db74\"\u003e.*\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eHTML_EXPR\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026lt;li\u0026gt;\u0026lt;a href=\\\u0026#34;\\3\\\u0026#34;\u0026gt;\\1\u0026lt;\\/a\u0026gt; (\u0026lt;a href=\\\u0026#34;\\2\\\u0026#34;\u0026gt;feed\u0026lt;\\/a\u0026gt;)\u0026lt;\\/li\u0026gt;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eREPLACE_EXPR\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s/\u003c/span\u003e$XML_EXPR\u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e$HTML_EXPR\u003cspan style=\"color:#e6db74\"\u003e/g\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egrep \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;xmlUrl\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | sed \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$REPLACE_EXPR\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.12.18",
    "tags": ["Blogging"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "913b5fa85507"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/capturing-quoted-string-sed/",
    "url": "https://brandonrozek.com/blog/capturing-quoted-string-sed/",
    "title": "Capturing Quoted Strings in Sed",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cem\u003eDisclaimer: This posts assumes some knowledge about regular expressions.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eRecently I was trying to capture an HTML attribute in \u003ccode\u003esed\u003c/code\u003e. For example, let\u0026rsquo;s say I want to extract the \u003ccode\u003ehref\u003c/code\u003e attribute in the following example:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e\u0026lt;a href=\u0026#34;https://brandonrozek.com\u0026#34; rel=\u0026#34;me\u0026#34;\u0026gt;\u0026lt;/a\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAdvice you commonly see on the Internet is to use a capture group for anything between the quotes of the href.\u003c/p\u003e\n\u003cp\u003eIn regular expression land, we can represent anything as \u003ccode\u003e.*\u003c/code\u003e and define a capture group of some regular expression \u003ccode\u003eX\u003c/code\u003e as \u003ccode\u003e\\(X\\)\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esed \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s/.*href=\\\u0026#34;\\(.*\\)\\\u0026#34;.*/\\1/g\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhat does this look like for our input?\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#ae81ff\"\u003e\\\u0026lt;\u003c/span\u003ea href\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\\u0026#34;\u003c/span\u003ehttps://brandonrozek.com\u003cspan style=\"color:#ae81ff\"\u003e\\\u0026#34;\u003c/span\u003e rel\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\\u0026#34;\u003c/span\u003eme\u003cspan style=\"color:#ae81ff\"\u003e\\\u0026#34;\\\u0026gt;\\\u0026lt;\u003c/span\u003e/a\u003cspan style=\"color:#ae81ff\"\u003e\\\u0026gt;\u003c/span\u003e |\u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003esed \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s/.*href=\\\u0026#34;\\(.*\\)\\\u0026#34;.*/\\1/g\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ehttps://brandonrozek.com\u0026#34; rel=\u0026#34;me\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIt matches all the way until the second \u003ccode\u003e\u0026quot;\u003c/code\u003e! What we want, is to not match \u003cem\u003eany\u003c/em\u003e character within the quotations, but match any character that is not the quotation itself \u003ccode\u003e[^\\\u0026quot;]*\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esed \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s/.*href=\\\u0026#34;\\([^\\\u0026#34;]*\\)\\\u0026#34;.*/\\1/g\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis then works for our example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#ae81ff\"\u003e\\\u0026lt;\u003c/span\u003ea href\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\\u0026#34;\u003c/span\u003ehttps://brandonrozek.com\u003cspan style=\"color:#ae81ff\"\u003e\\\u0026#34;\u003c/span\u003e rel\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\\u0026#34;\u003c/span\u003eme\u003cspan style=\"color:#ae81ff\"\u003e\\\u0026#34;\\\u0026gt;\\\u0026lt;\u003c/span\u003e/a\u003cspan style=\"color:#ae81ff\"\u003e\\\u0026gt;\u003c/span\u003e |\u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003esed \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s/.*href=\\\u0026#34;\\([^\\\u0026#34;]*\\)\\\u0026#34;.*/\\1/g\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ehttps://brandonrozek.com\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWithin a bash script, we can make this a little more readable by using multiple variables.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eQUOTED_STR\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\\\u0026#34;\\([^\\\u0026#34;]*\\)\\\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eBEFORE_TEXT\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;.*href=\u003c/span\u003e$QUOTED_STR\u003cspan style=\"color:#e6db74\"\u003e.*\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAFTER_TEXT\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\\1\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eREPLACE_EXPR\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;s/\u003c/span\u003e$BEFORE_TEXT\u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e$AFTER_TEXT\u003cspan style=\"color:#e6db74\"\u003e/g\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eINPUT\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\\\u0026lt;a href=\\\u0026#34;https://brandonrozek.com\\\u0026#34; rel=\\\u0026#34;me\\\u0026#34;\\\u0026gt;\\\u0026lt;/a\\\u0026gt;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$INPUT\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | sed \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$REPLACE_EXPR\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.12.18",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "9801b2556737"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/drawing-trees-with-tikz-latex/",
    "url": "https://brandonrozek.com/blog/drawing-trees-with-tikz-latex/",
    "title": "Drawing Trees in LaTex with Tikz",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eFor the longest time I\u0026rsquo;ve been avoiding Tikz because I imagined it being too difficult to learn. Usually I create a graphic using a program like \u003ca href=\"https://draw.io\"\u003eDraw.IO\u003c/a\u003e and import it as an image. Though this time around, I decided that I\u0026rsquo;m going to learn how to make trees in Tikz. It turns out, it\u0026rsquo;s not as bad as I anticipated.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;m only going to provide a few simple examples in this post. To learn more check out the \u003ca href=\"https://tikz.dev/tikz-trees\"\u003eTikz documentation on trees\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eRemember to have \u003ccode\u003e\\usepackage{tikz}\u003c/code\u003e in the preamble.\u003c/p\u003e\n\u003cp\u003eTo create a Tikz figure, you\u0026rsquo;ll need to create a \u003ccode\u003etikzpicture\u003c/code\u003e environment\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{tikzpicture}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#75715e\"\u003e% Tikz Code Here\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{tikzpicture}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eEvery tree first begins with a root node.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{tikzpicture}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e\\node\u003c/span\u003e {Root Node};\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{tikzpicture}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe semicolon at the end denotes the end of a \u003ccode\u003etikz\u003c/code\u003e command.\u003c/p\u003e\n\u003cp\u003eNow let\u0026rsquo;s make the root node have one child node.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{tikzpicture}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e\\node\u003c/span\u003e {Root Node}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\tchild {node {Child Node}};\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{tikzpicture}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/20221206111634.png\" alt=\"Screenshot of tree with one node on each level\"\u003e\u003c/p\u003e\n\u003cp\u003eNotice that the text of the nodes are within the \u003ccode\u003e{}\u003c/code\u003e after the node command.\u003c/p\u003e\n\u003cp\u003eTo create another child for a node, place it in the same level as the existing child.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{tikzpicture}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e\\node\u003c/span\u003e {Root Node}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\tchild {node {Left Child}}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\tchild {node {Right Child}};\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{tikzpicture}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/20221206111922.png\" alt=\"Screenshot of tree with the root node having two child nodes\"\u003e\u003c/p\u003e\n\u003cp\u003eThe rendered tree may have text overlap as shown in the last screenshot. This is where \u003ccode\u003etikz\u003c/code\u003e options come in. We can define the separation distance between siblings. I don\u0026rsquo;t have many tips for choosing the value other than to play around and see how it looks.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{tikzpicture}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    level 1/.style={sibling distance=25mm}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e\\node\u003c/span\u003e {Root Node}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\tchild {node {Left Child}}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\tchild {node {Right Child}};\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{tikzpicture}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/20221206112150.png\" alt=\"Improved screenshot of previous tree but with well separated child nodes\"\u003e\u003c/p\u003e\n\u003cp\u003eTo show how the child nesting works, I\u0026rsquo;ll finish by giving the right child two children nodes.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{tikzpicture}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    level 1/.style={sibling distance=25mm},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    level 2/.style={sibling distance=15mm},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e\\node\u003c/span\u003e {Root Node}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\tchild {node {Left Child}}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\tchild {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t    node {Right Child}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t    child {node {Child A}}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t    child {node {Child B}}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t};\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{tikzpicture}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/20221206112444.png\" alt=\"Screenshot of a tree similar to the previous tree, but with the right child having two child nodes one with the label A and the other with the label B.\"\u003e\u003c/p\u003e\n",
    "date_published": "2022.12.06",
    "tags": ["LaTex"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "f80065fbf92f"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/deploying-hugo-website-through-gh-actions/",
    "url": "https://brandonrozek.com/blog/deploying-hugo-website-through-gh-actions/",
    "title": "Deploying my Hugo Website through GitHub Actions",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eFor the longest time I\u0026rsquo;ve held out on deploying my website through GitHub actions. My rationale at the time was:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eIf I have to execute \u003ccode\u003egit push\u003c/code\u003e, I might as well run a \u003ccode\u003e./sync\u003c/code\u003e script afterwards.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eWhat convinced me otherwise is automated commits. I currently have GitHub actions that sync my \u003ca href=\"/toots/\"\u003eMastodon toots\u003c/a\u003e and \u003ca href=\"/observations/\"\u003eiNaturalist observations\u003c/a\u003e. As part of the sync process, a git commit is made. This commit should then trigger a site rebuild.\u003c/p\u003e\n\u003cp\u003eHow do we create a GitHub action that builds a Hugo website and deploys it via \u003ccode\u003ersync\u003c/code\u003e? The rest of this post will go over the components of the GitHub action that triggers when I update my website.\u003c/p\u003e\n\u003ch2 id=\"triggers\"\u003eTriggers\u003c/h2\u003e\n\u003cp\u003eI currently have three triggers for my deployment GitHub action:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eManual (\u003ccode\u003eworkflow_dispatch\u003c/code\u003e)\u003c/li\u003e\n\u003cli\u003ePushes to the \u003ccode\u003emain\u003c/code\u003e branch\u003c/li\u003e\n\u003cli\u003eDaily schedule via \u003ccode\u003ecron\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yml\" data-lang=\"yml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eon\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eworkflow_dispatch\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003epush\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ebranches\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003emain\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eschedule\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#f92672\"\u003ecron\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;21 11 * * *\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"steps\"\u003eSteps\u003c/h2\u003e\n\u003cp\u003eI call my job \u003ccode\u003ebuild_and_publish\u003c/code\u003e and have it run on top of the latest Ubuntu image.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yml\" data-lang=\"yml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003ejobs\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003ebuild_and_publish\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eruns-on\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eubuntu-latest\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"step-1-checkout-the-repository\"\u003eStep 1: Checkout the Repository\u003c/h3\u003e\n\u003cp\u003eHere we can rely on Github\u0026rsquo;s \u003ccode\u003echeckout\u003c/code\u003e action to provide the latest version of the code.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yml\" data-lang=\"yml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003esteps\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  - \u003cspan style=\"color:#f92672\"\u003ename\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eCheckout\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003euses\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eactions/checkout@v3\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ewith\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003esubmodules\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003efetch-depth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSince my website relies on submodules, we need to make sure that its included in the checkout. The \u003ccode\u003efetch-depth\u003c/code\u003e flag denotes how many commits to retrieve. By default (\u003ccode\u003efetch-depth: 1\u003c/code\u003e) it only fetches the latest commit, however setting it to \u003ccode\u003e0\u003c/code\u003e retrieves all commits. This is needed for Hugo\u0026rsquo;s last modified feature to work.\u003c/p\u003e\n\u003ch3 id=\"step-2-update-the-submodules\"\u003eStep 2: Update the submodules\u003c/h3\u003e\n\u003cp\u003eEven though we checked out the whole repository with its associated submodules, they may be out of date. This step makes sure that we\u0026rsquo;re using the latest version of the submodule.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e- \u003cspan style=\"color:#f92672\"\u003ename\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eGit submodule update\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003erun\u003c/span\u003e: |\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    git pull --recurse-submodules\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    git submodule update --remote --recursive\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"step-3-setup-hugo\"\u003eStep 3: Setup Hugo\u003c/h3\u003e\n\u003cp\u003eSince Hugo is a static binary, we can pull it straight from their website.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e- \u003cspan style=\"color:#f92672\"\u003ename\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eSetup Hugo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eenv\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eHUGO_VERSION\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0.105.0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003erun\u003c/span\u003e: |\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    curl -L \u0026#34;https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_Linux-64bit.tar.gz\u0026#34; --output hugo.tar.gz\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    tar -xvzf hugo.tar.gz\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    sudo mv hugo /usr/local/bin\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"step-4-build-the-website\"\u003eStep 4: Build the website\u003c/h3\u003e\n\u003cp\u003eWe can use a separate step to build the website. This along with the deployment are among the few  places where this script can fail, so it\u0026rsquo;s nice to separate it out in case.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e- \u003cspan style=\"color:#f92672\"\u003ename\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eBuild Hugo Website\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003erun\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ehugo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"step-5-install-the-ssh-key\"\u003eStep 5: Install the SSH key\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e- \u003cspan style=\"color:#f92672\"\u003ename\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eInstall SSH Key\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003erun\u003c/span\u003e: |\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    install -m 600 -D /dev/null ~/.ssh/id_rsa\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    echo \u0026#34;${{ secrets.BUILD_SSH_KEY }}\u0026#34; \u0026gt; ~/.ssh/id_rsa\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    echo \u0026#34;${{ secrets.HOST_KEY }}\u0026#34; \u0026gt; ~/.ssh/known_hosts\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    echo \u0026#34;Host brandonrozek.com\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        Hostname brandonrozek.com\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        user build\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        IdentityFile ~/.ssh/id_rsa\u0026#34; \u0026gt; ~/.ssh/config\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAt this point in our script we need to handle \u003ca href=\"https://docs.github.com/en/actions/security-guides/encrypted-secrets\"\u003esecrets\u003c/a\u003e. The post I linked to will likely have the most up to date information, but as of this time of writing, you can add secrets by going to the \u003ccode\u003eSettings\u003c/code\u003e tab of the repository. A secret is a key-value pair,  therefore to access your secret in the GH action, you need to reference the key.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e${{ secrets.YOUR_KEY_HERE }}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe need secrets for the SSH key used to deploy the website and the known hosts file so that I don\u0026rsquo;t have to do host verification. The first line ensures that the permissions of the SSH key is correct, and the last line makes it so that the \u003ccode\u003ersync \u003c/code\u003e command within my \u003ccode\u003esync.sh\u003c/code\u003e script is simpler. I use my \u003ccode\u003esync.sh\u003c/code\u003e not only in the next step of this action but on my own machine which has a different config associated with it.\u003c/p\u003e\n\u003ch3 id=\"step-6-deployment\"\u003eStep 6: Deployment\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e- \u003cspan style=\"color:#f92672\"\u003ename\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eDeploy\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003erun\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e./deploy.sh\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn my repository there is a \u003ccode\u003edeploy.sh\u003c/code\u003e with the following contents\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/usr/bin/env sh\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003ersync -Pazc --exclude\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e*.bak --delete public/ build@brandonrozek.com:brandonrozek/\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis syncs everything within the \u003ccode\u003epublic\u003c/code\u003e build folder up to my webserver excluding files ended in \u003ccode\u003e.bak\u003c/code\u003e and removing any files on the webserver that aren\u0026rsquo;t in the build folder.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eOther than the \u003ccode\u003echeckout\u003c/code\u003e action, each step does not depend on an external library  to provide the functionality. I think it\u0026rsquo;s important to implement each of the steps ourselves, as opposed to relying on a \u003ccode\u003ehugo\u003c/code\u003e GH action library or a \u003ccode\u003eSFTP\u003c/code\u003e library. Not only does this safeguard us against supply side attacks, it also makes these actions more portable. I am not counting on GitHub to always allow the usage of their build infrastructure for free.\u003c/p\u003e\n\u003cp\u003eGitHub action in its entirety:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003ename\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eBuild and Deploy Hugo Website\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eon\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eworkflow_dispatch\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003epush\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ebranches\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003emain\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eschedule\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#f92672\"\u003ecron\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;21 11 * * *\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003ejobs\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003ebuild_and_publish\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eruns-on\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eubuntu-latest\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003esteps\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#f92672\"\u003ename\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eCheckout\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003euses\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eactions/checkout@v3\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003ewith\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003esubmodules\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003efetch-depth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#f92672\"\u003ename\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eGit submodule update\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003erun\u003c/span\u003e: |\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e          git pull --recurse-submodules\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e          git submodule update --remote --recursive\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#f92672\"\u003ename\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eSetup Hugo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eenv\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003eHUGO_VERSION\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0.105.0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003erun\u003c/span\u003e: |\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e          curl -L \u0026#34;https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_Linux-64bit.tar.gz\u0026#34; --output hugo.tar.gz\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e          tar -xvzf hugo.tar.gz\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e          sudo mv hugo /usr/local/bin\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#f92672\"\u003ename\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eBuild Hugo Website\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eid\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ebuild\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003erun\u003c/span\u003e: |\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e          hugo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#f92672\"\u003ename\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eInstall SSH Key\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003erun\u003c/span\u003e: |\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e          install -m 600 -D /dev/null ~/.ssh/id_rsa\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e          echo \u0026#34;${{ secrets.BUILD_SSH_KEY }}\u0026#34; \u0026gt; ~/.ssh/id_rsa\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e          echo \u0026#34;${{ secrets.HOST_KEY }}\u0026#34; \u0026gt; ~/.ssh/known_hosts\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e          echo \u0026#34;Host brandonrozek.com\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e              Hostname brandonrozek.com\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e              user build\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e              IdentityFile ~/.ssh/id_rsa\u0026#34; \u0026gt; ~/.ssh/config\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#f92672\"\u003ename\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eDeploy\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003erun\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e./deploy.sh\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.12.04",
    "tags": ["Hugo"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "b08e82293bd2"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pretty-rss-feeds/",
    "url": "https://brandonrozek.com/blog/pretty-rss-feeds/",
    "title": "Pretty RSS Feeds",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eHi, I have a RSS feed. This allows you to subscribe to my words without me even knowing. How does it work? Well you need an RSS link, here\u0026rsquo;s mine:\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://brandonrozek.com/blog/index.xml\"\u003ehttps://brandonrozek.com/blog/index.xml\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eThen you need to input this URL into a \u003cem\u003enewsreader\u003c/em\u003e application (\u003ca href=\"https://feedly.com/\"\u003eFeedly\u003c/a\u003e, \u003ca href=\"https://feedbin.com\"\u003eFeedbin\u003c/a\u003e, \u003ca href=\"https://netnewswire.com/\"\u003eNetNewsWire\u003c/a\u003e, \u003ca href=\"https://en.wikipedia.org/wiki/Comparison_of_feed_aggregators\"\u003emany others\u003c/a\u003e) which checks to see if there are any changes in the XML file and presents it in a visually nice way.\u003c/p\u003e\n\u003cp\u003eIn fact, many websites come with a RSS feed. You don\u0026rsquo;t even need to know the exact URL for it to work, if you type in \u003ccode\u003ehttps://brandonrozek.com\u003c/code\u003e into your newsreader, you\u0026rsquo;ll get options for my various different feeds.\u003c/p\u003e\n\u003cp\u003eHow does that work? Well the website creator needs to include a tag in their website HTML.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003elink\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erel\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;alternate\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#a6e22e\"\u003etype\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;application/rss+xml\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#a6e22e\"\u003ehref\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://brandonrozek.com/blog/index.xml\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#a6e22e\"\u003etitle\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon Rozek\u0026#39;s Blog\u0026#34;\u003c/span\u003e /\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eMost RSS feeds don\u0026rsquo;t come styled. Therefore if you click on the RSS link you\u0026rsquo;ll see a bunch of XML formatted text. That can be confusing for people when they first visit, can we do better?\u003c/p\u003e\n\u003ch2 id=\"about-feeds-rss-style\"\u003eAbout Feeds RSS Style\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://interconnected.org/\"\u003eMatt Webb\u003c/a\u003e created \u003ca href=\"https://aboutfeeds.com\"\u003ehttps://aboutfeeds.com\u003c/a\u003e in order to provide a friendly overview into the world of RSS. As part of that he includes a style called \u003ca href=\"https://github.com/genmon/aboutfeeds/blob/main/tools/pretty-feed-v3.xsl\"\u003epretty-feed.xsl\u003c/a\u003e. The file comes with instructions built in, but it\u0026rsquo;s mostly as simple as adding a style tag in the beginning of the XML\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u0026lt;?xml-stylesheet href=\u0026#34;/PATH-TO-YOUR-STYLES/pretty-feed-v3.xsl\u0026#34; type=\u0026#34;text/xsl\u0026#34;?\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI hope more bloggers incorporate the style into their feed. I\u0026rsquo;m waiting for the day that the largest blogging platform Wordpress includes it by default.\u003c/p\u003e\n",
    "date_published": "2022.12.04",
    "tags": ["Blogging"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "5eb712e808ae"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/decentralized-currency-and-issues-with-custodians/",
    "url": "https://brandonrozek.com/blog/decentralized-currency-and-issues-with-custodians/",
    "title": "The Promise of Decentralized Currency and the Issues with Custodians",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cem\u003eDisclaimer: I\u0026rsquo;m not an active participant in the Bitcoin community and the comments of this post are from an outside perspective.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://en.wikipedia.org/wiki/FTX_(company)\"\u003eFTX, one of the largest crypto exchanges\u003c/a\u003e, collapsed last month taking many people\u0026rsquo;s investments and savings with them. Investigations are currently underway for if FTX \u003ca href=\"https://www.nytimes.com/article/ftx-bankruptcy-crypto-collapse.html\"\u003eillegally lent customer funds to a partner company Alameda research\u003c/a\u003e, the beginning of the strategies used by FTX to hide their insolvency.\u003c/p\u003e\n\u003cp\u003eAs this is among a \u003ca href=\"https://www.nerdwallet.com/article/investing/crypto-winter\"\u003eseries of collapses\u003c/a\u003e, it has people skeptical on the value of Bitcoin. From my memory, the last major collapse of similar magnitude is that of \u003ca href=\"https://en.wikipedia.org/wiki/Mt._Gox\"\u003eMt. Gox\u003c/a\u003e in 2014. Even with these collapses, I however, don\u0026rsquo;t see this as the reason to avoid Bitcoin altogether.\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003ch2 id=\"who-owns-the-bitcoin\"\u003eWho owns the Bitcoin?\u003c/h2\u003e\n\u003cp\u003eWhen you buy Bitcoin off any of these exchanges you don\u0026rsquo;t \u003cstrong\u003eown\u003c/strong\u003e the Bitcoin. What do I mean by this? Well let\u0026rsquo;s look at a higher level on how Bitcoin works.\u003c/p\u003e\n\u003cp\u003eFrom the original whitepaper:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eEach owner transfers the coin to the next by digitally signing a hash of the previous transaction and the public key of the next owner and adding these to the end of the coin.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eWhat do you need then to perform the transaction?\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eYour private key\u003c/li\u003e\n\u003cli\u003eDestination\u0026rsquo;s public key\u003c/li\u003e\n\u003cli\u003ePublic ledger which denotes the funds associated with your account\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eNow where is your private key stored on these exchanges? It terms out, as a user of their service, you don\u0026rsquo;t have access to it. Instead you\u0026rsquo;re granting them permission to handle the money on your behalf, what you receive is much like an IOU.\u003c/p\u003e\n\u003cp\u003eNow we trust banks to hold our funds, how is this different? Banks are FDIC/NCUA insured. This means that you are guaranteed to receive $250,000 per insured bank, per depositor, per account ownership category. If your bank ever goes bankrupt, all your money does not go with it.\u003c/p\u003e\n\u003cp\u003eWhat this is more similar to is the PayPal model. \u003ca href=\"https://www.forbes.com/advisor/banking/paypal-as-bank-account/\"\u003ePayPal is itself not a bank\u003c/a\u003e, so leaving money in a PayPal acount is not protected under FDIC insurance. In fact, they are also under a \u003ca href=\"https://arstechnica.com/tech-policy/2022/01/paypal-stole-users-money-after-freezing-seizing-funds-lawsuit-alleges/\"\u003eclass action lawsuit for freezing customer funds\u003c/a\u003e. Nothing says that you don\u0026rsquo;t own the money more than not being able to access it.\u003c/p\u003e\n\u003ch2 id=\"bitcoin-is-meant-to-be-a-decentralized-currency\"\u003eBitcoin is meant to be a decentralized currency\u003c/h2\u003e\n\u003cp\u003eWe really shouldn\u0026rsquo;t be holding large amounts of Bitcoins with these \u003cem\u003ecustodians\u003c/em\u003e. Instead, each person should control their own private keys. We can do this through a non-custodial wallet. This requires the generation of private keys which are often done through \u003cem\u003eseed words\u003c/em\u003e. I won\u0026rsquo;t go into detail on this as per my disclaimer above, however, I find \u003ca href=\"https://bitcoiner.guide/\"\u003ebitcoiner.guide\u003c/a\u003e to be a nice and honest resource.\u003c/p\u003e\n\u003ch2 id=\"do-not-confuse-ownership-with-privacy\"\u003eDo not confuse ownership with privacy\u003c/h2\u003e\n\u003cp\u003eOften purchasing Bitcoin from an exchange passes through a \u003cem\u003eKnow Your Customer\u003c/em\u003e (KYC) organization. Simply moving the money off a custodial account into your own Bitcoin wallet does not mean that it can not be associated with you. \u003ca href=\"https://www.blockchain.com/explorer/blocks/bch\"\u003eBitcoin transactions are public information by design\u003c/a\u003e. In terms of controlling your privacy, I can\u0026rsquo;t provide much advice. However, look into Bitcoin Escrow services to facillitate anonymous purchasing/selling as well as the \u003ca href=\"https://bitcoinmagazine.com/technical/a-comprehensive-bitcoin-coinjoin-guide\"\u003ecoin join techinque\u003c/a\u003e for granting some plausible deniability.\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eI understand other arguments more such as the un-sustainability of the \u003cem\u003eProof-of-work\u003c/em\u003e protocol.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2022.12.04",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "88190b25cbc4"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/goaccess/",
    "url": "https://brandonrozek.com/blog/goaccess/",
    "title": "Visualizing Nginx logs with GoAccess",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI currently use \u003ca href=\"https://ackee.electerious.com/\"\u003eAckee\u003c/a\u003e to collect minimal analytics on this website. That comes as an additional Javascript library which has its own set of possible issues:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eDoesn\u0026rsquo;t count visits with javascript disabled\u003c/li\u003e\n\u003cli\u003eSome visitors may block the API\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eOn the other hand, nginx which is my webserver of choice comes with the capability of logging requests. In fact, when setting it up as a podman/docker container, it already is configured to do so.\u003c/p\u003e\n\u003ch2 id=\"configuring-nginx\"\u003eConfiguring nginx\u003c/h2\u003e\n\u003cp\u003eTo see where it\u0026rsquo;s configured within the container, take a look at \u003ccode\u003e/config/nginx/nginx.conf\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eaccess_log\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/config/log/nginx/access.log\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf it\u0026rsquo;s not already there, you can add it to configure logging. In fact, you can also change the logging location, however, the rest of this post will assume the one listed.\u003c/p\u003e\n\u003cp\u003eIf we\u0026rsquo;re using \u003ccode\u003enginx\u003c/code\u003e within a container, then we\u0026rsquo;ll need a volume mount to keep the log data persistent. Here is how we do it via \u003ccode\u003edocker-compose\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003enginx\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e# Other attributes\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003evolumes\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Other volume mounts\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003e/var/log/nginx:/config/log/nginx\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWithin the container, the logs are stored in \u003ccode\u003e/config/log/nginx\u003c/code\u003e. This configuration then maps it so it shows up at \u003ccode\u003e/var/log/nginx\u003c/code\u003e on the host system.\u003c/p\u003e\n\u003cp\u003eOkay so we are now logging to \u003ccode\u003eaccess.log\u003c/code\u003e, how do these logs look like? By default it looks like the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e60.37.199.123 - - [27/Nov/2022:02:04:44 -0500] \u0026#34;GET /offline HTTP/2.0\u0026#34; 301 162 \u0026#34;https://brandonrozek.com/serviceworker.js\u0026#34; \u0026#34;Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/87.0.4280.141 Safari/537.36 Edg/87.0.664.75\u0026#34;\n60.37.199.123 - - [27/Nov/2022:02:04:44 -0500] \u0026#34;GET /offline HTTP/2.0\u0026#34; 301 162 \u0026#34;https://brandonrozek.com/serviceworker.js\u0026#34; \u0026#34;Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/87.0.4280.141 Safari/537.36 Edg/87.0.664.75\u0026#34;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow it\u0026rsquo;s difficult to parse a log file filled with entries like this. This is where \u003ccode\u003eGoAccess\u003c/code\u003e comes to play.\u003c/p\u003e\n\u003ch2 id=\"goaccess\"\u003eGoAccess\u003c/h2\u003e\n\u003cp\u003eGoAccess is a real-time web log analyzer which displays in both a terminal and browser visitor metrics.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/goaccess-dashboard.png\" alt=\"GoAccess Screenshot\"\u003e\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve been running it for the past week, and there\u0026rsquo;s a section where it displays pages that are 404ing.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20221201233536.png\" alt=\"Screenshot of pages that are 404ing\"\u003e\u003c/p\u003e\n\u003cp\u003eThis has inspired me to fix my theme and upload a \u003ccode\u003erobots.txt\u003c/code\u003e!\u003c/p\u003e\n\u003cp\u003eBy default, GoAccess is not a daemon, and will start by parsing the log file at its current point. To start it, all you need to specify is the location of the log file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egoaccess /var/log/nginx/access.log\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen, it will ask you some questions about the type of log that it\u0026rsquo;s parsing. To save those options permanently, we\u0026rsquo;ll need to edit \u003ccode\u003e/etc/goaccess/goaccess.conf\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThese are the settings for the default \u003ccode\u003enginx\u003c/code\u003e log type.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003elog-file /var/log/nginx/access.log\n\ntime-format %H:%M:%S\ndate-format %d/%b/%Y\n\n# NCSA Combined Log Format\nlog-format %h %^[%d:%t %^] \u0026#34;%r\u0026#34; %s %b \u0026#34;%R\u0026#34; \u0026#34;%u\u0026#34;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eOther options to consider:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e# Prompt log/date configuration window on program start.\nconfig-dialog false\n\n# Color highlight active panel.\nhl-header true\n\n# Ignore request\u0026#39;s query string.\n# i.e.,  www.google.com/page.htm?query =\u0026gt; www.google.com/page.htm\n#\n# Note: Removing the query string can greatly decrease memory\n# consumption, especially on timestamped requests.\nno-query-string true\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"proscons-to-log-analyzing\"\u003ePros/Cons to Log Analyzing\u003c/h2\u003e\n\u003cp\u003ePros:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eNo database to manage\u003c/li\u003e\n\u003cli\u003eNo extra software to install\u003c/li\u003e\n\u003cli\u003eNo javascript dependency\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eCons:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eBot traffic may overshadow regular visitors\u003c/li\u003e\n\u003cli\u003eMore complex logging behavior can\u0026rsquo;t be replicated\n\u003cul\u003e\n\u003cli\u003eThough from a human privacy standpoint, this is a good thing :)\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"things-to-look-at-later\"\u003eThings to look at later\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;ve been only using GoAccess for the past week now, and there are still many features that I haven\u0026rsquo;t tried out. I hope to update this post as I play around.\u003c/p\u003e\n\u003ch3 id=\"custom-logging-formats\"\u003eCustom Logging Formats\u003c/h3\u003e\n\u003cp\u003eNginx allows us to customize the log format so that we only record the bits we care about. This can save on both disk space and time to parse if done correctly.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s an example from the \u003ca href=\"https://docs.nginx.com/nginx/admin-guide/monitoring/logging/\"\u003eNginx Documentation\u003c/a\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ehttp\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elog_format\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ecompression\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u003c/span\u003e$remote_addr \u003cspan style=\"color:#e6db74\"\u003e-\u003c/span\u003e $remote_user \u003cspan style=\"color:#e6db74\"\u003e[\u003c/span\u003e$time_local] \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                           \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u0026#34;\u003c/span\u003e$request\u0026#34; $status $body_bytes_sent \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                           \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u0026#34;\u003c/span\u003e$http_referer\u0026#34; \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$http_user_agent\u0026#34; \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$gzip_ratio\u0026#34;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"report-generation\"\u003eReport Generation\u003c/h3\u003e\n\u003cp\u003eGoAccess allows for generation of reports. This can be a nice visualization such as HTML, as well as outputs to CSV and JSON for more programatic manipulation.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s a JSON report for the GoAccess website itself:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;general\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e:\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;date_time\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2016-11-22 21:48:12 -0600\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;total_requests\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e248660\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;valid_requests\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e248660\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;failed_requests\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;generation_time\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e20\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unique_visitors\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e22953\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unique_files\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e14622\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;excluded_hits\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unique_referrers\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e5587\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unique_not_found\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e994\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;unique_static_files\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e10521\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;log_size\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e64570946\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;bandwidth\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3650756631\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;log_path\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/var/log/nginx/access.log\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can do many things with a JSON export of website traffic, but one of my favorite ideas is to have a \u0026ldquo;Most Popular Posts\u0026rdquo; section of my website.\u003c/p\u003e\n",
    "date_published": "2022.12.01",
    "tags": ["Nginx"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "be642b59857"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/python-check-submodule-load/",
    "url": "https://brandonrozek.com/blog/python-check-submodule-load/",
    "title": "Quick Python: Check Submodule Load",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eReading other people\u0026rsquo;s code is a great way to learn. Recently, I was looking at the \u003ccode\u003enumpy\u003c/code\u003e repository and they had a hidden gem within their \u003ccode\u003esetup.py\u003c/code\u003e. In large scale repositories like \u003ccode\u003enumpy\u003c/code\u003e, we can have different dependencies that we rely upon via \u003cem\u003egit submodules\u003c/em\u003e. The following function will check to see that these dependencies are loaded and in a \u0026ldquo;clean\u0026rdquo; state.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003echeck_submodules\u003c/span\u003e():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34; verify that the submodules are checked out and clean\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        use `git submodule update --init`; on failure\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eexists(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;.git\u0026#39;\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e open(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;.gitmodules\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e f:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e line \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e f:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;path\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e line:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                p \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e line\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esplit(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;=\u0026#39;\u003c/span\u003e)[\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e]\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003estrip()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eexists(p):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eValueError\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Submodule \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e missing\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eformat(p))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    proc \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e subprocess\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ePopen([\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;git\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;submodule\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;status\u0026#39;\u003c/span\u003e],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            stdout\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003esubprocess\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ePIPE)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    status, _ \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e proc\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ecommunicate()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    status \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e status\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003edecode(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ascii\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;replace\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e line \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e status\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esplitlines():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e line\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003estartswith(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;-\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003eor\u003c/span\u003e line\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003estartswith(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;+\u0026#39;\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eValueError\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Submodule not clean: \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eformat(line))\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo get a better idea of what this piece of code is doing, let\u0026rsquo;s trace through its execution. The \u003ccode\u003e.gitmodules\u003c/code\u003e file defines where each of the submodules are stored in the repository. Here\u0026rsquo;s the file for \u003ccode\u003enumpy\u003c/code\u003e\u0026rsquo;s repository.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[submodule \u0026#34;doc/source/_static/scipy-mathjax\u0026#34;]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003epath\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003edoc/source/_static/scipy-mathjax\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\turl = https://github.com/scipy/scipy-mathjax.git\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[submodule \u0026#34;numpy/core/src/umath/svml\u0026#34;]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003epath\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003enumpy/core/src/umath/svml\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\turl = https://github.com/numpy/SVML.git\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe function then checks if each of the \u003ccode\u003epath\u003c/code\u003e locations are defined in the filesystem.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check #1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eexists(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;doc/source/_static/scipy-mathjax\u0026#34;\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eValueError\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Submodule \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e missing\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eformat(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;doc/source/_static/scipy-mathjax\u0026#34;\u003c/span\u003e))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check #2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e os\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eexists(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;numpy/core/src/umath/svml\u0026#34;\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eValueError\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Submodule \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e missing\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eformat(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;numpy/core/src/umath/svml\u0026#34;\u003c/span\u003e))\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow it is possible for these submodules to be checked out, but be in some sort of unclean state. For example let\u0026rsquo;s say that I edit \u003ccode\u003enumpy/core/src/umath/svml/README.md\u003c/code\u003e and commit that change. Then the \u003ccode\u003egit submodule status\u003c/code\u003e command will look like the following.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$ git submodule status\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e36f4c898f2255e0c98eb6949cd67381552d5ffea ../doc/source/_static/scipy-mathjax \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eheads/master\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e+1f2b5b3cae704ffa6210a638dfd084946da366bf core/src/umath/svml \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eheads/main-1-g1f2b5b3\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNotice the \u003ccode\u003e+\u003c/code\u003e behind the second submodule. According to the man page for \u003ccode\u003egit submodule\u003c/code\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eShow the status of the submodules. This will print the SHA-1 of the currently checked out commit for each submodule, along with the submodule path and the output of git describe for the SHA-1.\u003c/p\u003e\n\u003cp\u003eEach SHA-1 will possibly be prefixed with\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e-\u003c/strong\u003e if the submodule is not initialized\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e+\u003c/strong\u003e if the currently checked out submodule commit does not match the SHA-1 found in the index of the containing repository\u003c/li\u003e\n\u003cli\u003eU if the submodule has merge conflicts.\u003c/li\u003e\n\u003c/ul\u003e\u003c/blockquote\u003e\n\u003cp\u003eThe existence of the \u003ccode\u003e+\u003c/code\u003e would make this check fail. However, no prefixes will make the check succeed.\u003c/p\u003e\n\u003cp\u003eBy executing this function within the repositories \u003ccode\u003esetup.py\u003c/code\u003e, it verifies that the needed dependencies are checked out and clean before installing the python package. Checks like these make python packages feel a little more stable.\u003c/p\u003e\n",
    "date_published": "2022.12.01",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "80a6f9baadf4"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/immutable-bfs-unfold/",
    "url": "https://brandonrozek.com/blog/immutable-bfs-unfold/",
    "title": "Immutable Traversals with Unfold",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLet\u0026rsquo;s consider the following binary tree:\u003c/p\u003e\n\n\n\n\u003cdiv class=\"goat svg-container \"\u003e\n  \n    \u003csvg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      font-family=\"Menlo,Lucida Console,monospace\"\n      \n        viewBox=\"0 0 736 121\"\n      \u003e\n      \u003cg transform='translate(8,16)'\u003e\n\u003cpath d='M 136,32 L 144,16' fill='none' stroke='currentColor'\u003e\u003c/path\u003e\n\u003cpath d='M 136,64 L 144,80' fill='none' stroke='currentColor'\u003e\u003c/path\u003e\n\u003cpath d='M 160,16 L 168,32' fill='none' stroke='currentColor'\u003e\u003c/path\u003e\n\u003ctext text-anchor='middle' x='128' y='52' fill='currentColor' style='font-size:1em'\u003eb\u003c/text\u003e\n\u003ctext text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'\u003ea\u003c/text\u003e\n\u003ctext text-anchor='middle' x='152' y='100' fill='currentColor' style='font-size:1em'\u003ec\u003c/text\u003e\n\u003ctext text-anchor='middle' x='176' y='52' fill='currentColor' style='font-size:1em'\u003ed\u003c/text\u003e\n\u003c/g\u003e\n\n    \u003c/svg\u003e\n  \n\u003c/div\u003e\n\u003cp\u003eWe can encode this with the following Scala code:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efinal\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eBinNode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e label\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eString\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e left\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eOption\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eBinNode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e],\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e right\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eOption\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eBinNode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Leaf Nodes\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e c_node \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eBinNode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;c\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eNone\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eNone\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e d_node \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eBinNode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;d\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eNone\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eNone\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Rest of nodes\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e b_node \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eBinNode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;b\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eNone\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eSome\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ec_node\u003cspan style=\"color:#f92672\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e a_node \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eBinNode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;a\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eSome\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eb_node\u003cspan style=\"color:#f92672\"\u003e),\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eSome\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ed_node\u003cspan style=\"color:#f92672\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor depth first search, an intuitive immutable implementation would be a recursive function.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Using Preorder traversal\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDFS\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003enode\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eBinNode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eBinNode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003elazy\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e left_side \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e node\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eleft\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efold\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eempty\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eBinNode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e])(\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eDFS\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003elazy\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e right_side \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e node\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eright\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efold\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eempty\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eBinNode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e])(\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eDFS\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003enode\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e left_side \u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e right_side\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLet\u0026rsquo;s evaluate this using our example above:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDFS\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ea_node\u003cspan style=\"color:#f92672\"\u003e).\u003c/span\u003etoList\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emap\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e_\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elabel\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// List(a, b, c, d)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe recursive implementation inherently uses the system stack to keep track of the nodes.  This means that the last element gets evaluated in each step. Otherwise called last-in-first-out (LIFO). Breadth first search, however, uses a queue based approach where the first one added to the data structure is the first one considered (FIFO).\u003c/p\u003e\n\u003cp\u003eTo preserve immutability in our code, we can use \u003ccode\u003eunfold\u003c/code\u003e. Here our state is the queue of nodes.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eBFS\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003enode\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eBinNode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eBinNode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eunfold\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003enode\u003cspan style=\"color:#f92672\"\u003e))(\u003c/span\u003eq \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e q\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eisEmpty then\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e crnt_node \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e q\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehead\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e next_q \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e q\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003etail \u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e crnt_node\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eleft \u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e crnt_node\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eright\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eSome\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ecrnt_node\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e next_q\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eEvaluating on our example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eBFS\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ea_node\u003cspan style=\"color:#f92672\"\u003e).\u003c/span\u003etoList\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emap\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e_\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elabel\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// List(a, b, d, c)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can also use \u003ccode\u003eunfold\u003c/code\u003e for the depth first search approach as well. We can replace the list used with a stack.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eimport\u003c/span\u003e scala.collection.mutable.Stack\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDFS2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003enode\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eBinNode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eBinNode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eunfold\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eStack\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003enode\u003cspan style=\"color:#f92672\"\u003e))(\u003c/span\u003es \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e s\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eisEmpty then\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e crnt_node \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e s\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epop\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            s\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epushAll\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ecrnt_node\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eright\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            s\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epushAll\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ecrnt_node\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eleft\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eSome\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ecrnt_node\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e s\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUsing a stack introduces some mutability. We can use the immutable list data structure instead, as long as we satisfy the LIFO ordering.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDFS3\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003enode\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eBinNode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eBinNode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eunfold\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003enode\u003cspan style=\"color:#f92672\"\u003e))(\u003c/span\u003es \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e s\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eisEmpty then\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e crnt_node \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e s\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elast\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e next_s \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e s\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003einit \u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e crnt_node\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eright \u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e crnt_node\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eleft\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eSome\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ecrnt_node\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e next_s\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.11.12",
    "tags": ["Functional Programming","Scala"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "ddffc5b14ef9"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/memoization-scala/",
    "url": "https://brandonrozek.com/blog/memoization-scala/",
    "title": "Memoization in Scala",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn a \u003ca href=\"/blog/corecursion-unfold-infinite-sequences/\"\u003erecent post\u003c/a\u003e, I talked about how corecursion is a great solution for removing redundant calculations. However if we\u0026rsquo;re sticking to a recursive approach, one way we can reduce redundancies is to use memoization. The idea here is that we save prior computations in some data structure and refer to them if requested.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://stackoverflow.com/a/36960228\"\u003ePathikrit on StackOverflow\u003c/a\u003e provided a great solution for Scala using hashmaps:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eimport\u003c/span\u003e scala.collection.mutable\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e memoize\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eI\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003eO\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e](\u003c/span\u003ef\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eI\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u0026gt;\u003c/span\u003e O\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eI\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u0026gt;\u003c/span\u003e O \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e mutable\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eHashMap\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eI\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003eO\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003eself \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eoverride\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e apply\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ekey\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eI\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esynchronized\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003egetOrElseUpdate\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ekey\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e f\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ekey\u003cspan style=\"color:#f92672\"\u003e)))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf the input is already a key in the hashmap, then we return its value. Otherwise we calculate the output via \u003ccode\u003ef\u003c/code\u003e and update the hashmap.\u003c/p\u003e\n\u003cp\u003eNow lets memoize say the Fibonacci sequence\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e fib \u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eInt\u003c/span\u003e  \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e memoize \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e n \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e fib\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003en \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e fib\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003en \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eCalling \u003ccode\u003efib(5)\u003c/code\u003e returns \u003ccode\u003e5\u003c/code\u003e. However, we can also see the saved computations by analyzing the hashmap \u003ccode\u003efib\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eHashMap(0 -\u0026gt; 0, 1 -\u0026gt; 1, 2 -\u0026gt; 1, 3 -\u0026gt; 2, 4 -\u0026gt; 3, 5 -\u0026gt; 5)\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2022.11.12",
    "tags": ["Scala"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "2558d6abd9b8"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/corecursion-unfold-infinite-sequences/",
    "url": "https://brandonrozek.com/blog/corecursion-unfold-infinite-sequences/",
    "title": "Corecursion, Unfold and Infinite Sequences",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eRecursion takes a large problem and breaks it down until it reaches some base cases. One popular example, is the factorial function.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e fact\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ex\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e x \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e then\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e x \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e then\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        x \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e fact\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ex \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThough we can similarly arrive at the answer by starting at the base case \u003ccode\u003e1\u003c/code\u003e and multiplying until we reach \u003ccode\u003ex\u003c/code\u003e. This is called co-recursion.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e1 * 2 * ... * x\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003ccode\u003eUnfold\u003c/code\u003e allows us to create sequences given some initial state and a function that takes some state and produces a value for the sequence. For the factorial function, we want to keep track of in our state the last factorial computed and the current index. \u003ccode\u003e(lastFact, currInd)\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eTherefore, our initial state is \u003ccode\u003e(1, 0)\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e fact_seq \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eunfold\u003cspan style=\"color:#f92672\"\u003e((\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e))((\u003c/span\u003ex\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e y\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eSome\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    x\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// currFact\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ex \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ey \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e),\u003c/span\u003e y \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// (nextFact, nextInd)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e fact \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ex\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e fact_seq\u003cspan style=\"color:#f92672\"\u003e().\u003c/span\u003etake\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ex \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e).\u003c/span\u003etoList\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elast\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLet\u0026rsquo;s trace an execution of \u003ccode\u003efact(4)\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efact(4)\nIterator.unfold((1, 0))((x, y) =\u0026gt; Some((x, (x * (y + 1), y + 1)))).take(5).toList.last\nStates: (1, 0) -\u0026gt; (1, 1) -\u0026gt; (2, 2) -\u0026gt; (6, 3) -\u0026gt; (24, 4).....\n[1, 1, 2, 6, 24, ...].take(4).toList.last\n[1, 1, 2, 6, 24].last\n24\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow why is this useful when maybe the recursive version can seem cleaner? Co-recursion and in turn unfolding can help remove redundancies. Let\u0026rsquo;s look at the Fibbonaci sequence for an example. The recursive version would be as follows:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e fib\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003en \u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e then\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e then\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        fib\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003en \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e fib\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003en \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow let\u0026rsquo;s trace through an execution of \u003ccode\u003efib(4)\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efib(4)\nfib(3) + fib(2)\n(fib(2) + fib(1)) + fib(2)\n((fib(1) + fib(0)) + fib(1)) + fib(2)\n((1 + fib(0)) + fib(1)) + fib(2)\n((1 + 0) + fib(1)) + fib(2)\n(1 + fib(1)) + fib(2)\n(1 + 1) + fib(2)\n2 + fib(2)\n2 + (fib(1) + fib(0))\n2  + (1 + fib(0))\n2 + (1 + 0)\n2 + 1\n3\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNotice how there are many redundant calculations, for example \u003ccode\u003efib(2)\u003c/code\u003e is evaluated twice separately in line 3 above.\u003c/p\u003e\n\u003cp\u003eNow lets look at how \u003ccode\u003eunfold\u003c/code\u003e helps.  For our state, we need to keep track of the last two evaluations. Therefore, we can represent our state as \u003ccode\u003e(currentAnswer, nextAnswer)\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e fib_seq \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eunfold\u003cspan style=\"color:#f92672\"\u003e((\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e))((\u003c/span\u003ex\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e y\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eSome\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ex\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ey\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e x \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e y\u003cspan style=\"color:#f92672\"\u003e)))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e fib\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003en\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e fib_seq\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003etake\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003en \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e).\u003c/span\u003etoList\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elast\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTracing through \u003ccode\u003efib(4)\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efib(4)\nIterator.unfold((0, 1))((x, y) =\u0026gt; Some(x,(y, x + y))).take(5).toList.last\nState: (0, 1) -\u0026gt; (1, 1) -\u0026gt; (1, 2) -\u0026gt; (2, 3) -\u0026gt; (3, 5)\n[0, 1, 1, 2, 3, ...].take(5).toList.last\n[0, 1, 1, 2, 3].last\n3\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"small-unfold-examples\"\u003eSmall Unfold Examples\u003c/h2\u003e\n\u003cp\u003eTo get a better handle of \u003ccode\u003eunfold\u003c/code\u003e. Here are three examples:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e(1) Build an iterator from start to infinity with a step size of \u003ccode\u003estep\u003c/code\u003e\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eBuilt-in way in Scala:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efrom\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003estart\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e step\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUsing \u003ccode\u003eunfold\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e fromStep\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e((\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e])\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003en\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e step\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eunfold\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003en\u003cspan style=\"color:#f92672\"\u003e)(\u003c/span\u003ex \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eSome\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e((\u003c/span\u003ex\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e x \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e step\u003cspan style=\"color:#f92672\"\u003e)))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003e(2) Build an infinite sequence of even numbers\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eUsing from and map:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e evens \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efrom\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e).\u003c/span\u003efilter\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e_\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e%\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUsing \u003ccode\u003efromStep\u003c/code\u003e in (1)\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e evens \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e fromStep\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUsing \u003ccode\u003eunfold\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e evens \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eunfold\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)(\u003c/span\u003ex \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eSome\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e((\u003c/span\u003ex\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e x \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003e(3)  Build a countdown from $n$ to $0$\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eNotice how the function within \u003ccode\u003eunfold\u003c/code\u003e needs to return an \u003ccode\u003eOption\u003c/code\u003e. If the returned option is \u003ccode\u003eNone\u003c/code\u003e then the sequence is terminated.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e countdown \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003en\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eunfold\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003en\u003cspan style=\"color:#f92672\"\u003e)(\u003c/span\u003ex \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e x \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e then \u003cspan style=\"color:#a6e22e\"\u003eNone\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eSome\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e((\u003c/span\u003ex\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e x \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003e(4) Repeat an element forever\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eFor this example, we don\u0026rsquo;t need to carry any state throughout hte computation.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e repeat\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e])\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003en\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eunfold\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eNone\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e_\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eSome\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003en\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eNone\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"recursive-sequences\"\u003eRecursive Sequences\u003c/h2\u003e\n\u003cp\u003eIn the past, \u003ca href=\"/blog/haskellrealsequences/\"\u003eI\u0026rsquo;ve written\u003c/a\u003e about analyzing sequences from real analysis within Haskell. Within it, I was looking at the following sequence:\n$$\nf(1) = 1, f(2) = 2, f(n) = \\frac{1}{2}(f(n - 2) + f(n - 1))\n$$\nThe technique I described in that post is to build out the function \u003ccode\u003ef\u003c/code\u003e and then map it to the natural numbers. In Scala that would look like:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e f\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eDouble\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e n \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e then \u003cspan style=\"color:#ae81ff\"\u003e1.0\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e then \u003cspan style=\"color:#ae81ff\"\u003e2.0\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0.5\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ef\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003en \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e f\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003en \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e f_sequence \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efrom\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e).\u003c/span\u003emap\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ef\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHowever as mentioned in prior in this post, this methodology is suboptimal since there will be many repeated computations.\u003c/p\u003e\n\u003cp\u003eCorecursion and unfold comes to the rescue again. For recursive sequences, we can make the state the base cases \u003ccode\u003e(1.0, 2.0)\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e f_sequence \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eIterator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eunfold\u003cspan style=\"color:#f92672\"\u003e((\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1.0\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2.0\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e))((\u003c/span\u003ex1\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e x2\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eSome\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    x1\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ex2\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0.5\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ex1 \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e x2\u003cspan style=\"color:#f92672\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can get a good guess at where this sequence converges by looking at the $100^{th}$ element.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ef_sequence\u003cspan style=\"color:#f92672\"\u003e().\u003c/span\u003etake\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e).\u003c/span\u003etoList\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elast\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// 1.6666666666666665\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you want to learn more about unfold or see a different take, then the following two blog posts helped me craft this one:\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://blog.genuine.com/2020/07/scala-unfold/\"\u003ehttps://blog.genuine.com/2020/07/scala-unfold/\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://john.cs.olemiss.edu/~hcc/csci555/notes/FPS05/Laziness.html\"\u003ehttps://john.cs.olemiss.edu/~hcc/csci555/notes/FPS05/Laziness.html\u003c/a\u003e\u003c/p\u003e\n",
    "date_published": "2022.11.12",
    "tags": ["Scala","Functional Programming"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "edd1ef8ee314"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/deep-recursion/",
    "url": "https://brandonrozek.com/blog/deep-recursion/",
    "title": "Deep Recursion in Functional Programming",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn functional programming, we often look at a list in terms of its head (first-element) and tail (rest-of-list). This allows us to define operations on a list recursively. For example, how do we sum a list of integers such as \u003ccode\u003e[1, 2, 3, 4]\u003c/code\u003e?\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e sum\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003el \u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e])\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e l\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esize \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e then\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e l\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esize \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e then\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        l\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehead\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        l\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehead \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e sum\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003el\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003etail\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe later learn that the \u003ccode\u003efold\u003c/code\u003e version is more compact.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003el\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efoldLeft\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e_\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e_\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe big question though, is how do we write this function if we allow lists to be arbitrarily nested? One example of this is the list \u003ccode\u003e[[1, 2, [3, 4]], 5, [[6, 7], 8]]\u003c/code\u003e\u003c/p\u003e\n\u003ch2 id=\"deep-recursion\"\u003eDeep Recursion\u003c/h2\u003e\n\u003cp\u003eTo accomplish this, we need to make use of \u003cem\u003edeep recursion\u003c/em\u003e. At its essence, we change the previous program so that it also recurses on the head of the list as well since that may be a list.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e deep_sum\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003el\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eMatchable\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e l\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eisInstanceOf\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e then\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        l\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003easInstanceOf\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e ll \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e l\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003easInstanceOf\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eMatchable\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e ll\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esize \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e then\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e ll\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esize \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e then\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            deep_sum\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ell\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehead\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            deep_sum\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ell\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehead\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e deep_sum\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ell\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003etail\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLets trace through an example \u003ccode\u003e[[1], 2]\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edeep_sum([[1], 2])\ndeep_sum([1]) + deep_sum([2])\ndeep_sum(1) + deep_sum([2])\n1 + deep_sum([2])\n1 + deep_sum(2)\n1 + 2\n3\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"deep-recursion-via-fold\"\u003eDeep Recursion via Fold\u003c/h2\u003e\n\u003cp\u003eSimilar to shallow recursion, we can use the \u003ccode\u003efoldLeft\u003c/code\u003e function to help clean up the code a little:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e deep_sum\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003el \u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eMatchable\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e l\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eisInstanceOf\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e then\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        l\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003easInstanceOf\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e ll \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e l\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003easInstanceOf\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eMatchable\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        ll\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efoldLeft\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)((\u003c/span\u003ec\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e n\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e c \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e deep_sum\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003en\u003cspan style=\"color:#f92672\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn the above fold, \u003ccode\u003ec\u003c/code\u003e contains the current partial result (of type \u003ccode\u003eInt\u003c/code\u003e) which we can then add the recursive result of the next element of the list.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s trace through an example \u003ccode\u003e[[1], 2]\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edeep_sum([[1], 2])\n[[1], 2].foldLeft(0)((c, n) =\u0026gt; c + deep_sum(n))\n(0 + deep_sum([1])) + deep_sum(2)\n(0 + [1].foldLeft(0)((c1, n1) =\u0026gt; c1 + deep_sum(n1))) + deep_sum(2)\n(0 + (0 + deep_sum(1))) + deep_sum(2)\n(0 + (0 + 1)) + deep_sum(2)\n(0 + 1) + deep_sum(2)\n1 + deep_sum(2)\n1 + 2\n3\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"deep-recursion-via-foldmap\"\u003eDeep Recursion via Fold/Map\u003c/h2\u003e\n\u003cp\u003eIn the prior example, the deep recursion and the reduction logic were combined within the same anonymous function. We can separate this out by making use of \u003ccode\u003emap\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e deep_sum\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003el\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eMatchable\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e l\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eisInstanceOf\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e then\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        l\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003easInstanceOf\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e ll \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e l\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003easInstanceOf\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eMatchable\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        l\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emap\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003edeep_sum\u003cspan style=\"color:#f92672\"\u003e).\u003c/span\u003efoldLeft\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e_\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e_\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIntuitively, the map will apply \u003ccode\u003edeep_sum\u003c/code\u003e to each element of the list and returns an \u003ccode\u003eInt\u003c/code\u003e for each element as that\u0026rsquo;s the return type of \u003ccode\u003edeep_sum\u003c/code\u003e. Once we have our list of integers, we can perform the fold to reduce it to a single sum.\u003c/p\u003e\n\u003cp\u003eLets trace through an example \u003ccode\u003e[[1], 2]\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edeep_sum([[1], 2])\n[deep_sum([1]), deep_sum(2)].foldLeft(0)(_ + _)\n[[deep_sum(1)].foldLeft(0)(_ + _), deep_sum(2)].foldLeft(0)(_ + _)\n[[1].foldLeft(0)(_ + _), deep_sum(2)].foldLeft(0)(_ + _)\n[(0 + 1), deep_sum(2)].foldLeft(0)(_ + _)\n[1, deep_sum(2)].foldLeft(0)(_ + _)\n[1, 2].foldLeft(0)(_ + _)\n(0 + 1) + 2\n1 + 2\n3\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2022.11.11",
    "tags": ["Scala","Functional Programming"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "3515de0ab3a1"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/different-views-of-fold/",
    "url": "https://brandonrozek.com/blog/different-views-of-fold/",
    "title": "Different Views of Fold and Their Combinations",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eFold is a functional programming pattern that operates over some sequence with a binary operation and a starting value. There are two variants:\u003c/p\u003e\n\u003cp\u003eFoldLeft: Performs the binary operation from the left of the sequence to the right.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e).\u003c/span\u003efoldLeft\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e_\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e_\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// ((((0 + 1) + 2) + 3) + 4)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// 10\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFoldRight: Performs the binary operation from the right of the sequence to the left.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e).\u003c/span\u003efoldRight\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e_\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e_\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// (1 + (2 + (3 + (4 + 0))))\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// 10\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe two fold variants only return the same solution if the function being applied is associative. Which is the case for integer addition above.\u003c/p\u003e\n\u003ch2 id=\"definition-and-the-two-views\"\u003eDefinition and the Two Views\u003c/h2\u003e\n\u003cp\u003eDefinition of \u003ccode\u003efoldLeft\u003c/code\u003e within Scala:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e foldLeft\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eB\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e](\u003c/span\u003ez\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eB\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)(\u003c/span\u003eop\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eB\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eA\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e B\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eB\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ematch\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e seq\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eIndexedSeq\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eA\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e@unchecked\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e foldl\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eseq\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e z\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e op\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e_\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e result \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e z\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e it \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e iterator\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eit\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehasNext\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      result \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e op\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eresult\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e it\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003enext\u003cspan style=\"color:#f92672\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    result\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e foldl\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eB\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e](\u003c/span\u003eseq\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eIndexedSeq\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eA\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e],\u003c/span\u003e start\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e z\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eB\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e op\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eB\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eA\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e B\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eB\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003e@tailrec\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e loop\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eat\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e end\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e acc\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eB\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eB\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eat \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e end\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e acc\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e loop\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eat \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e end\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e op\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eacc\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e seq\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eat\u003cspan style=\"color:#f92672\"\u003e)))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    loop\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003estart\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e seq\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elength\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e z\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNotice that this includes both a recursive and iterative definition within one function! Since most people start off by learning loops, let\u0026rsquo;s focus on the iterative implementation.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e result \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e z\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eit\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehasNext\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    result \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e op\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eresult\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e it\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003enext\u003cspan style=\"color:#f92672\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"combining-folds\"\u003eCombining Folds\u003c/h2\u003e\n\u003cp\u003eWhen building out complex functions we may want to loop multiple times:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eWhile loops next to each other ($n_1 + n_2$ complexity)\u003c/li\u003e\n\u003cli\u003eWhile loop within a while loop ($n_1 * n_2$ complexity)\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"n_1--n_2-complexity\"\u003e$n_1 + n_2$ Complexity\u003c/h3\u003e\n\u003cp\u003eThe following is an example\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e of this class of algorithms.\u003c/p\u003e\n\u003cp\u003eGiven a list of words, title case each word and combine them into a single sentence.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e l1 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;steve\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;is\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;doing\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;great\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e title \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003es\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eString\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e s\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esubstring\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e).\u003c/span\u003etoUpperCase \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e s\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esubstring\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e it1 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e l1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eiterator\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e id1 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eempty\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eString\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eit1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehasNext\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    id1 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e id1 \u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003etitle\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eit1.next\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e it2 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e id1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eiterator\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e id2 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eit2\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehasNext\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    id2 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e id2 \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e it2\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003enext\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eConverting to fold solution:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e l1 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;steve\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;is\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;doing\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;great\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e title \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003es\u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eString\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e s\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esubstring\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e).\u003c/span\u003etoUpperCase \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e s\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esubstring\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e id1 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eempty\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eString\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e id2 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003el1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efoldLeft\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eid1\u003cspan style=\"color:#f92672\"\u003e)((\u003c/span\u003ec\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e n\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tc \u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003etitle\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003en\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e).\u003c/span\u003efoldLeft\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eid2\u003cspan style=\"color:#f92672\"\u003e)((\u003c/span\u003ec\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e n\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tc \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e n\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"n_1--n_2-complexity-1\"\u003e$n_1 * n_2$ Complexity\u003c/h3\u003e\n\u003cp\u003eAn example of this class of programs is the \u003ca href=\"https://en.wikipedia.org/wiki/Cartesian_product\"\u003eCartesian product\u003c/a\u003e\n$$\n(1, 2, 3) \\times (4, 5,6) \\rightarrow ((1,4), (1,5), (1,6), \\dots, (3,4), (3,5), (3,6))\n$$\nIterative Implementation:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e listInput1 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e listInput2 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e6\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e id_outer \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eempty\u003cspan style=\"color:#f92672\"\u003e[(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e it1 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e listInput1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eiterator\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eit1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehasNext\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e id_inner \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eempty\u003cspan style=\"color:#f92672\"\u003e[(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e n1 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e it1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003enext\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e it2 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e listInput2\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eiterator\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eit2\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehasNext\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e n2 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e it2\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003enext\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        result_inner \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e result_inner \u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003en1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003en2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    result \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e result \u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e result_inner\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eConverting to fold solution:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e l1 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e l2 \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e6\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e id_outer \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eempty\u003cspan style=\"color:#f92672\"\u003e[(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eval\u003c/span\u003e id_inner \u003cspan style=\"color:#66d9ef\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eempty\u003cspan style=\"color:#f92672\"\u003e[(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003el1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efoldLeft\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eid_outer\u003cspan style=\"color:#f92672\"\u003e)((\u003c/span\u003ec1\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e n1\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    c1 \u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e l2\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efoldLeft\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eid_inner\u003cspan style=\"color:#f92672\"\u003e)((\u003c/span\u003ec2\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e n2\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        c2 \u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003en1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003en2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eThanks Clare for thinking of this example!\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2022.11.09",
    "tags": ["Scala","Functional Programming"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "13bdb8690a4c"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/fold-not-only-reduces/",
    "url": "https://brandonrozek.com/blog/fold-not-only-reduces/",
    "title": "Fold Not Only Reduces",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOne misconception when first learning about fold is that it takes a list of elements of a certain type (\u003ccode\u003eList[T]\u003c/code\u003e) and \u0026ldquo;reduces\u0026rdquo; it to a single item of type \u003ccode\u003eT\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThis misconception is aided by one of the most common fold examples: summing a list.\u003c/p\u003e\n\u003cp\u003eScala Example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e).\u003c/span\u003efoldLeft\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)((\u003c/span\u003ec\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e n\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e c \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e n\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Returns 15\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHaskell Example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-haskell\" data-lang=\"haskell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003efoldl\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e) \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e [\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e-- Returns 15\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHowever, let us look more closely at the type signature of \u003ccode\u003efoldLeft\u003c/code\u003e on a list of type \u003ccode\u003eX\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eHaskell:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e(B -\u0026gt; X -\u0026gt; B) -\u0026gt; B -\u0026gt; [X] -\u0026gt; B\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eScala:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e(id: B)(op: (B, X) =\u0026gt; B): B\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThere are a few things we can note here:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThe return type is not influenced by the list type \u003ccode\u003eX\u003c/code\u003e at all.\u003c/li\u003e\n\u003cli\u003eThe return type must match the type of the id of the fold.\u003c/li\u003e\n\u003cli\u003eThe operation takes two arguments, with the first type matching the id/start (\u003ccode\u003eB\u003c/code\u003e) and the second type matching the type within the list (\u003ccode\u003eX\u003c/code\u003e)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eTo show an example of how we don\u0026rsquo;t need to \u0026ldquo;reduce\u0026rdquo;, let\u0026rsquo;s return the elements of a list that\u0026rsquo;s greater than 5.\u003c/p\u003e\n\u003cp\u003eScala Example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scala\" data-lang=\"scala\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e7\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e8\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e9\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e).\u003c/span\u003efoldLeft\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eList\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eempty\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eInt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e])((\u003c/span\u003ec\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e n\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e then c \u003cspan style=\"color:#66d9ef\"\u003e:\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003en\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ec\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Returns List(7, 8, 9)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHaskell Example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-haskell\" data-lang=\"haskell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003el5\u003c/span\u003e c n \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e c \u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e [n] \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e c\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003efoldl\u003c/span\u003e l5 \u003cspan style=\"color:#66d9ef\"\u003e[]\u003c/span\u003e [\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e7\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e8\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e9\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e-- Returns [7,8,9]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.11.09",
    "tags": ["Functional Programming","Scala","Haskell"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "b419f7d25313"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/notes-beamer-latex/",
    "url": "https://brandonrozek.com/blog/notes-beamer-latex/",
    "title": "Speaker Notes in LaTex Beamer",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI often struggle with deciding how much content to put on my slides. Personally, I feel that my slides should be self-contained so that others can review them afterwards. This was especially true when I held \u003ca href=\"/ta/spring2022/csci2600/\"\u003erecitations\u003c/a\u003e as a TA.\u003c/p\u003e\n\u003cp\u003eWhat if instead of putting all the content into one slide, we can have a corresponding notes document? Crazy enough, Beamer comes to the rescue!\u003c/p\u003e\n\u003cp\u003eWithin a frame in beamer, we can add a \u003ccode\u003e\\note[item]{X}\u003c/code\u003e call, and the contents of \u003ccode\u003eX\u003c/code\u003e will appear in a bullet point within a corresponding speaker notes slide.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s see how this works in action.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\documentclass\u003c/span\u003e{beamer}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e%\\setbeameroption{hide notes} % Only slides\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e%\\setbeameroption{show only notes} % Only notes\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\setbeameroption\u003c/span\u003e{show notes on second screen=right} \u003cspan style=\"color:#75715e\"\u003e% Both\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\title\u003c/span\u003e{Speaker notes within Beamer}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\author\u003c/span\u003e{}\u003cspan style=\"color:#66d9ef\"\u003e\\date\u003c/span\u003e{}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{document}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{frame}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003e\\titlepage\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003e\\note\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[item]\u003c/span\u003e{Welcome to the talk!}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003e\\note\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[item]\u003c/span\u003e{As you can see, this slidedeck is a work in progress.}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{frame}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{document}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis code results in:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20221025220326.png\" alt=\"Image of slide with speaker notes shown on the right side\"\u003e\u003c/p\u003e\n\u003cp\u003eIn this mode, the slide is shown on the left side of the page while the speaker notes on shown on the right side. The comments in the code suggest that this isn\u0026rsquo;t the only way to do this! You can produce a PDF version with only the slides and one with only the notes as well!\u003c/p\u003e\n\u003cp\u003eWith this, I am thinking of distributing slides that I\u0026rsquo;m presenting more ahead of time. There are several benefits that I can immediately see in using the speaker notes feature:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eGet to keep slides minimal\u003c/li\u003e\n\u003cli\u003eKeep answers away from the live audience, with an easy way to distribute aftewards\u003c/li\u003e\n\u003cli\u003eNot forget any points you want to address\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIf you want to learn more features within LaTex beamer, I suggest checking out their \u003ca href=\"http://mirrors.ctan.org/macros/latex/contrib/beamer/doc/beameruserguide.pdf\"\u003epackage documentation\u003c/a\u003e. It\u0026rsquo;s surprisingly written accessibly, with many presentation tips within.\u003c/p\u003e\n",
    "date_published": "2022.10.25",
    "tags": ["LaTex"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "9643b7743f0c"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/optionality-risk-aversion/",
    "url": "https://brandonrozek.com/blog/optionality-risk-aversion/",
    "title": "Optionality and Risk in Decision Making",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOne technique for making decisions in uncertain situations is to look for \u003cem\u003eoptionality\u003c/em\u003e. This is one of the tips given in Russ Robert\u0026rsquo;s book \u003ca href=\"https://www.worldcat.org/title/1321820629\"\u003eWild problems: a guide to the decisions that define us\u003c/a\u003e. Let\u0026rsquo;s start off with the two definitions listed on \u003ca href=\"https://en.wiktionary.org/wiki/optionality\"\u003eWiktionary\u003c/a\u003e at my time of writing.\u003c/p\u003e\n\u003ch2 id=\"definitions-and-formalisms\"\u003eDefinitions and Formalisms\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003col\u003e\n\u003cli\u003e(finance, business) The value of additional optional investment opportunities available only after having made an initial investment.\u003c/li\u003e\n\u003cli\u003eQuality of state in which choice or discretion is allowed.\u003c/li\u003e\n\u003c/ol\u003e\u003c/blockquote\u003e\n\u003cp\u003eTo get to the meanings in the context of decision theory, we\u0026rsquo;ll unpack these two definitions individually.\u003c/p\u003e\n\u003ch3 id=\"for-definition-1\"\u003eFor definition (1):\u003c/h3\u003e\n\u003cp\u003eAfter having made an action, additional actions are made available as well as its associated value or utility.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s call $A_t$ the action set at time $t$. After having made some action $a$, the action set $A_{t + 1}$ contains the actions in $A_t$ as well as some additional actions $a_{n + 1}, \\dots, a_{n + m}$. The values for the new state action are also known, e.g $V(s, a_{n + 1}), \\dots, V(s, a_{n + m})$.\u003c/p\u003e\n\u003cp\u003eIn this definition, optionality gives us additional actions and more information.\u003c/p\u003e\n\u003ch3 id=\"for-definition-2\"\u003eFor definition (2):\u003c/h3\u003e\n\u003cp\u003eIn the current state, the agent is free to choose any of the available actions. In other words, they are not obligated to choose any particular action.\u003c/p\u003e\n\u003ch3 id=\"from-the-book\"\u003eFrom the book:\u003c/h3\u003e\n\u003cp\u003eNow let\u0026rsquo;s discuss how the book uses the word optionality.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eOptionality is when you have the freedom to do something but not the obligation.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eIn the context of decision making, what do we consider the obligation of a choice or action? One way to approach this is through the lens of automated planning. Within that field, each action has a precondition and effect. For an action to be applicable (or available to choose), the precondition must be met. Once you perform the action, the effects are made true.\u003c/p\u003e\n\u003cp\u003eWe can then roughly define obligation as the commitment to the effects of an action.\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e In other words, after getting an effect from the performance of an action, we do not change the effect later on.\u003c/p\u003e\n\u003ch4 id=\"vampire-problem\"\u003eVampire Problem\u003c/h4\u003e\n\u003cp\u003eNow consider the \u003ca href=\"/blog/value-shift/\"\u003eVampire problem\u003c/a\u003e. If an agent $\\mathfrak{a}$ who is currently human performs the action $a_{transform}$, the agent loses the property human and becomes a vampire. In the context of this problem, there is no going back. The agent cannot change from being a vampire. In other words, the agent is committed to being a vampire. Thus, the agent is obligated after transforming.\u003c/p\u003e\n\u003cp\u003eIn a more technical sense, obligation enforces monotonicity. If an agent is obligated, once a property is gained, it cannot be lost.\u003c/p\u003e\n\u003cp\u003eMany choices, however, do not incur an obligation. Let\u0026rsquo;s say I perform the action of moving my coffee mug from the living room to the kitchen. I am not obligated after performing that action. In fact, I\u0026rsquo;m free to move it back to the living room.\u003c/p\u003e\n\u003ch4 id=\"revertability\"\u003eRevertability\u003c/h4\u003e\n\u003cp\u003eThis gets to a property that is \u003cem\u003estronger\u003c/em\u003e than optionality but bears mentioning. Revertability.\u003c/p\u003e\n\u003cp\u003eIn this context, an action $a$ is revertible if the agent can perform a sequence of actions that \u003cem\u003eundos\u003c/em\u003e the effects of $a$.  Non-obligation (in our definition) is weaker since we only need to be able to change one effect of $a$.\u003c/p\u003e\n\u003cp\u003eMy proposed formalism of the book\u0026rsquo;s definition is different than the other two formalisms. If I had to categorize it, I would say that it is a stricter form of (2).\u003c/p\u003e\n\u003ch2 id=\"risk-aversion\"\u003eRisk Aversion\u003c/h2\u003e\n\u003cp\u003eParts of optionality can be captured by risk aversion. Though I have not formally considered the topic yet, my preliminary thoughts go in two directions when considering risk:\u003csup id=\"fnref:2\"\u003e\u003ca href=\"#fn:2\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e2\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eMaximize the possible actions available in the future.\u003c/li\u003e\n\u003cli\u003eMinimize negative consequences\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eIn the face of uncertainty, how do we maximize the possible actions available in the future? One solution I propose is to choose actions that are revertable.\u003c/p\u003e\n\u003cp\u003eWhy? Let\u0026rsquo;s say that we\u0026rsquo;re in a state $s_t$ and the available actions are ${a, b, c}$. Now let\u0026rsquo;s say we take action $a$ which takes us to state $s_{t + 1}$. Within the new state, the available actions are ${d, e, f}$ where applying $d$ takes us back to state $s_t$. Notice that the action $a$ is revertable. Performing the action unlocked additional actions while keeping $b,c$ only a revert action ($d$) away.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eWhen reasoning about an uncertain situation, consider selecting the action that does not commit you to the effects of said action. Look for actions that are revertable, as that expands the possible choices in the future.\u003c/p\u003e\n\u003cp\u003eAcknowledgements: Thanks to Clare for helping me understand the definitions from Wiktionary.\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eThere are other ways we can define obligation. In particular, we can take into account each individual effect as opposed to it collectively. Though we\u0026rsquo;ll leave that for another time.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:2\"\u003e\n\u003cp\u003eI do not discuss minimizing negative consequences in this post as it has little connection to the concept of optionality.\u0026#160;\u003ca href=\"#fnref:2\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2022.10.12",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/value-shift/",
    "url": "https://brandonrozek.com/blog/value-shift/",
    "title": "Explaining the Shift in Values",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn the book \u003ca href=\"https://www.worldcat.org/title/1321820629\"\u003eWild problems : a guide to the decisions that define us\u003c/a\u003e, Russ Roberts analyzes decision making on difficult life problems. These types of problems don\u0026rsquo;t have straightforward or measurable goals, and there\u0026rsquo;s no set procedure for success. Examples of these problems are choosing whether to marry, have kids, or switch jobs.\u003c/p\u003e\n\u003cp\u003eRuss Roberts is an economist, however, he starts the book\nby showing how economic principles cannot be easily\napplied to arrive at a decision for these problems.\nEconomic principles are grounded in the utilitarian approach to ethics. In it, a utility function is defined which measures how much benefits outweighs the costs within a certain situation.\u003c/p\u003e\n\u003cp\u003eIn a mathematical formulation, we would define it like the following:\n$$\nu_\\mathfrak{a}(s) \\rightarrow \\mathbb{R}\n$$\u003c/p\u003e\n\u003cp\u003eIf the utility is positive, then we say that there are more benefits than downsides. If negative, then vice versa. As differing agents have different preferences,\nthe utility function is dependent upon the agent $\\mathfrak{a}$. The input of the function is some \u003cem\u003estate\u003c/em\u003e $s$ which represents the environment that the agent is in. The output is a continuous real number.\u003c/p\u003e\n\u003cp\u003eA rational action in this context is the one that provides the highest utility in the next state.\n$$\na_{rational} = argmax_a(u_\\mathfrak{a}(P(s,a)))\n$$\nIn the last equation $P(s, a)$ provides the state that the agent finds themselves in after performing the action $a$ in their current state $s$.\u003c/p\u003e\n\u003cp\u003eIn sequential decision making, a popular formalism is the Markov Decision Process. This defines a tuple $\\langle S,A, P, R \\rangle$ where:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e$S$ represents the set of states\u003c/li\u003e\n\u003cli\u003e$A$ represents the set of actions\u003c/li\u003e\n\u003cli\u003e$P$ represents the transition function between states via the actions\u003c/li\u003e\n\u003cli\u003e$R$ represents the reward function based on a state and action\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eInitially, I\u0026rsquo;ll talk about deterministic MDPs. This means that the state after performing a state and action pair is always the same.\u003c/p\u003e\n\u003cp\u003eOne popular approach to solving these problems is through value iteration. This approach is characterized by the Bellman Equation:\n$$\nV(s) = max_a(R(s,a) + \\gamma V(P(s,a)))\n$$\nThis says that the value of the current state is the highest combination possible between the direct reward for some action $a$ and the discounted value of the next state.\u003c/p\u003e\n\u003cp\u003eRuss Roberts didn\u0026rsquo;t mention MDPs in his book, but I think he had something similar in mind when he writes about the difficulty of computing utility given a wild problem. One example he uses, comes from L.A. Paul and her book \u003ca href=\"https://www.worldcat.org/title/872342141\"\u003eTransformative Experience\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThe Vampire Problem\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eBefore you become a vampire, you can\u0026rsquo;t really imagine what it will be like. Your current experience doesn\u0026rsquo;t include what it\u0026rsquo;s like to subsist on blood and sleep in a coffin when the sun is shining. Sounds dreary? But most, maybe all, of the vampires you meet speak quite highly of the experience. Surveys of vampires reveal a high degree of happiness.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eRuss then remarks on one of his key points.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eOne of the weirdest parts of the decision, as Paul pints out, is that once you become a vampire, what you like and what you dislike change.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003eLet\u0026rsquo;s look at it in terms of value iteration. The value of becoming a vampire is equal to the reward during the initial transformation plus the discounted value of living life after the transformation.\n$$\nV(s_{human}, a_{transform}) = R(s_{human}, a_{transform}) + \\gamma V(P(s_{human}, a_{transform}))\n$$\nWe know that the state after transforming as a human is becoming a vampire so,\n$$\nV(s_{human}, a_{transform})  = R(s_{human}, a_{transform}) + \\gamma V(s_{vampire})\n$$\nThe issue, Russ implies, is that an agent has no way of approximating $V(s_{human}, a_{transform})$.\u003c/p\u003e\n\u003cp\u003eGiven the formalism, what are some possible reasons why we don\u0026rsquo;t know this?\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eThe agent does not know the action it performed.\u003c/li\u003e\n\u003cli\u003eThe agent doesn\u0026rsquo;t fully know the function $R$.\u003c/li\u003e\n\u003cli\u003eThe agent does not have knowledge about the entire state.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"not-knowing-the-action\"\u003eNot Knowing the Action\u003c/h2\u003e\n\u003cp\u003eWhen an agent performs the action $a_{transform}$ does it know that it performed that specific action? One way of looking at this is to see if the agent is able to tell apart any two arbitrary actions, say $a_{transform}$ and $a_{morph}$. I don\u0026rsquo;t believe this problem is well studied in literature. Do let me know if you know of any literature covering an agent confusing actions.\u003c/p\u003e\n\u003cp\u003eIn the case of the vampire problem, it\u0026rsquo;s likely safe to assume that the human knows the transformation action.\u003c/p\u003e\n\u003ch2 id=\"not-knowing-r\"\u003eNot Knowing $R$\u003c/h2\u003e\n\u003cp\u003ePreviously, we assumed that the agent knows\nthe reward for any given state action pair.\nNow let\u0026rsquo;s relax that assumption. One way we can\ndo that is by considering non-deterministic\nMDPs.\u003c/p\u003e\n\u003cp\u003eImagine there is a slot machine that either provides\n\\$20 or consumes your \\$1 coin with some probability.\nTraditionally, we would represent the reward function\nas an expected value.\u003c/p\u003e\n\u003cp\u003e$$\nR(s, a) = \\Sigma_{p, s^\\prime \\in P(s, a)}{(p * R(s,a,s^\\prime))}\n$$\u003c/p\u003e\n\u003cp\u003eIn many real world situations, the probabilities ($p$) of\nstates are not specified. One popular approach to\nthis issue is by sampling. If we can simulate the outcomes, then we can roughly know the probabilities\nof each transition with some level of certainty.\u003c/p\u003e\n\u003cp\u003eSimulating the state transition may be impossible.\nIn my daily life, I often make use of testimonial\nevidence when making decisions. These can serve\nas a rough estimation of value, though the states\nof the other party may not line up exactly with yours.\u003c/p\u003e\n\u003ch2 id=\"partial-observability\"\u003ePartial Observability\u003c/h2\u003e\n\u003cp\u003eAre we able to perceive the entirety of\na state? If not, within the field of\nsequential decision making, we call the\nproblem \u003cem\u003epartially observable\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eTwo reasons immediately come to mind for why partial observability exists.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eCompromised Perception: A state in which the agent\u0026rsquo;s perception only captures a subset of the total information. For example, heavy fog covers the roadway\nlimiting visibility of other traffic.\u003c/li\u003e\n\u003cli\u003eGroup Decision Making: Agents often don\u0026rsquo;t have insight to other\u0026rsquo;s thought processes or perceptions.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eOne popular way of modeling this is through\nthe introduction of belief states. A belief\nstate encapsulates the set of possible states.\nFor example, let\u0026rsquo;s say that I prefer heads on a\nquarter than tails. I don\u0026rsquo;t know what my friend\nprefers, but I can capture the possibilities in a\nbelief state.\u003c/p\u003e\n\u003cp\u003e$$\nb = { (Me_{heads}, Friend_{heads}), (Me_{heads}, Friend_{tails}) }\n$$\u003c/p\u003e\n\u003cp\u003eThe hope is that by keeping track of the\nlist of possible states, when we are interacting\nwith the environment, we can filter out impossible\ntransitions until we get to a single possibility.\nHowever, there\u0026rsquo;s no guarantee that this will occur.\nAlso the initial belief state may be infinitely long.\u003c/p\u003e\n\u003cp\u003eAnother way that we can hope to tackle this issue\nis by only focusing on important information in the\nstate or observation. Many details in our daily lives\nare irrelevant for a specific task at hand.\nWe can perform a state reduction in order to have a\nsmaller set of possible states to work with.\nThis involves identifying what pieces of information\nare important to a task, and what we can disregard.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eOverall this book has been a great read so far\nand has gotten me thinking about decision making\nin a general sense again. There are some promising\nresearch directions that come from this:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eHow can you approximate a reward from testimonial evidence?\u003c/li\u003e\n\u003cli\u003eTechniques for performing state reduction and modeling transitions via these reductions.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThank you to both Clare and James for helping me brainstorm the ideas in this post.\u003c/p\u003e\n",
    "date_published": "2022.10.11",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/accidentally-reinforcing-model-predictions-inaturalist-seek/",
    "url": "https://brandonrozek.com/blog/accidentally-reinforcing-model-predictions-inaturalist-seek/",
    "title": "Accidentally Reinforcing Model Predictions in iNaturalist with Seek",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI \u003ca href=\"/blog/identifying-plants-with-inaturalist/\"\u003epreviously wrote\u003c/a\u003e about using Seek to identify organisms and then uploading them separately to iNaturalist. Though after some thought I believe that by only uploading photos that Seek has blessed as knowing,  I may be reinforcing peculiar features of the dataset and hence the model.\u003c/p\u003e\n\u003cp\u003eFor example, let\u0026rsquo;s say that I\u0026rsquo;m using the iNaturalist app and I\u0026rsquo;m having difficulties getting the species classification. After shifting the angles of my shots, I finally get to one where it classifies the species. So, I snap a picture. What about all the other angles? Are those invalid for identifying the organism?\u003c/p\u003e\n\u003cp\u003eNow it\u0026rsquo;s possible that many of the other angles don\u0026rsquo;t capture enough information to separate the specific species from the rest in its genus. For example to my untrained eye, many species of the \u003ca href=\"https://www.inaturalist.org/taxa/48678-Solidago/browse_photos\"\u003eGoldenrod genus\u003c/a\u003e look the same. It could be that by zooming in with enough detail, there is enough information about the shape of the flower to make the classification. Or maybe you need a zoomed out view to see the shape of the leaves and stalk in comparison.\u003c/p\u003e\n\u003cp\u003eDoes this mean that uploading photos classified by Seek is redundant? No! Because there are a couple extra features outside the image itself.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTime:\u003c/strong\u003e It\u0026rsquo;s useful to know the lifecycle of the organism. Is it currently flowering? Is it dormant for the winter?\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eLocation:\u003c/strong\u003e Is the plant native to the area? Is it an invasive species or introduced? Location data can help identify clusters of organisms within an area.\u003c/p\u003e\n\u003cp\u003eFor the image itself, one recommendation I have is the following. Take the image that Seek recommends for the classification, and then take one or two different angles of the same organism. Within iNaturalist, you can upload multiple photos for a single organism.\u003c/p\u003e\n\u003cp\u003eNow go and take some pictures :)\u003c/p\u003e\n",
    "date_published": "2022.10.04",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "6ecb481df869"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/identifying-plants-with-inaturalist/",
    "url": "https://brandonrozek.com/blog/identifying-plants-with-inaturalist/",
    "title": "Identifying Plants With iNaturalist",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eSeek by iNaturalist (\u003ca href=\"https://play.google.com/store/apps/details?id=org.inaturalist.seek\"\u003eandroid\u003c/a\u003e) (\u003ca href=\"https://apps.apple.com/us/app/seek-by-inaturalist/id1353224144\"\u003eapple\u003c/a\u003e) is a great app for identifying plants and animals. It uses a computer vision model to provide the hierarchical classifications. In fact they run yearly challenges with their dataset on \u003ca href=\"https://www.kaggle.com/c/inaturalist-2021/overview\"\u003eKaggle\u003c/a\u003e as well as publicly host their dataset on AWS (\u003ca href=\"https://github.com/inaturalist/inaturalist-open-data\"\u003einstructions\u003c/a\u003e).\u003c/p\u003e\n\u003cp\u003eWhat makes the experience so interesting, is the identification process is somewhat interactive. For example, look at the following screenshots of when I was trying to identify a plant near my workplace.\u003c/p\u003e\n\u003cp\u003eFirst we start off with the dicot class. This is roughly equivalent to it saying that it has identified a flower.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20220929194646.jpg\" alt=\"Screenshot showing Dicot Class for the Flower\"\u003e\u003c/p\u003e\n\u003cp\u003eTo get a better classification, we need to get closer to the plant and try different angles, notice how the classification updates the get closer and closer to the species of the plant. That is, white snakeroot.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20220929194647.jpg\" alt=\"Screenshot showing Family designation for the Flower\"\u003e\u003cimg src=\"/files/images/blog/20220929194648.jpg\" alt=\"Screenshot showing Genus designation for the Flower\"\u003e\u003cimg src=\"/files/images/blog/20220929194649.jpg\" alt=\"Screenshot showing Species designation for the Flower\"\u003e\u003c/p\u003e\n\u003cp\u003eFun fact, white snakeroot is the most common flower in New York.\u003c/p\u003e\n\u003cp\u003eOnce you get to the species level, you can take a picture and the observation is saved within the Seek app. This by default is only saved locally and not shared with the community. If you\u0026rsquo;re up for some citizen science, we can upload it separately! If you like to use your phone for that you can use the iNaturalist app (\u003ca href=\"https://play.google.com/store/apps/details?id=org.inaturalist.android\"\u003eAndroid\u003c/a\u003e) (\u003ca href=\"https://apps.apple.com/us/app/inaturalist/id421397028\"\u003eApple\u003c/a\u003e).\u003c/p\u003e\n\u003cp\u003eWithin the app, create a new observation and select \u0026ldquo;choose image\u0026rdquo;. Then navigate to your Seek folder and select the image you want to upload.\u003c/p\u003e\n\u003cp\u003eYou should then see a screen similar to this one:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20220929201902.jpg\" alt=\"Screenshot showing new observation page where the user can fill out the species information and other notes.\"\u003e\u003c/p\u003e\n\u003cp\u003eIf your image contains geotagged information then it will use that to populate the location field. You can also change the location visibility to obscured or private instead of public if you\u0026rsquo;d like. If you\u0026rsquo;re not in a park or preserve, but instead at a garden or other areas with hand-placed plants you should mark it as cultivated. Though if you need more detail, the \u003ca href=\"https://www.inaturalist.org/pages/help-inaturalist-canada-en#captive\"\u003eiNaturalist FAQ\u003c/a\u003e explains it in depth.\u003c/p\u003e\n\u003cp\u003eWhen you click on \u0026ldquo;What did you see?\u0026rdquo;, you\u0026rsquo;ll see a new screen with the level of depth they were able to identify it at and nearby organisms that are visually similar.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20220929201903.jpg\" alt=\"Screenshot showing most specific classification made and visually similar and nearby species.\"\u003e\u003c/p\u003e\n\u003cp\u003eOnce you select the species you\u0026rsquo;re ready to publish the observation! This will generate a unique URL for the observation. For example, my \u003ca href=\"https://www.inaturalist.org/observations/135665018\"\u003ewhite snakeroot observation\u003c/a\u003e.  If another community member agrees with your classification and the observation has enough supporting metadata, then it could become research grade!\u003c/p\u003e\n\u003cp\u003eResearch grade observations gets used by scientists for research which may also help improve the image classification algorithm for all. Happy identifying!\u003c/p\u003e\n",
    "date_published": "2022.09.29",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "bc9fd04fff31"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/podman-nginx-tcpv6-http2-ready/",
    "url": "https://brandonrozek.com/blog/podman-nginx-tcpv6-http2-ready/",
    "title": "Getting Podman and Nginx TCPv6 and HTTP/2 Ready",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen checking the status of my website, I discovered that my website wasn\u0026rsquo;t accessible over IPV6 and didn\u0026rsquo;t have HTTP/2 enabled! This post will go over how I remedied that with my current setup of Nginx running within a podman container. Do note, TCPv6 will only work if you are provided a IPv6 address from your server provider.\u003c/p\u003e\n\u003ch2 id=\"local-verification\"\u003eLocal Verification\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eFor IPv6:\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eA website that I used for checking if my site was accessible over IPv6 is \u003ca href=\"https://ready.chair6.net/\"\u003ehttps://ready.chair6.net/\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eThough there are also an array of linux tools you can use.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eCheck if the DNS records contain a \u003ccode\u003eAAAA\u003c/code\u003e record\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eLet\u0026rsquo;s look at my website as an example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edig brandonrozek.com AAAA\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOutput:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e; \u0026lt;\u0026lt;\u0026gt;\u0026gt; DiG 9.18.1-1ubuntu1.2-Ubuntu \u0026lt;\u0026lt;\u0026gt;\u0026gt; brandonrozek.com AAAA\n;; global options: +cmd\n;; Got answer:\n;; -\u0026gt;\u0026gt;HEADER\u0026lt;\u0026lt;- opcode: QUERY, status: NOERROR, id: 24248\n;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 65494\n;; QUESTION SECTION:\n;brandonrozek.com.              IN      AAAA\n\n;; ANSWER SECTION:\nbrandonrozek.com.       2       IN      AAAA    2600:3c03::f03c:93ff:fe89:8753\n\n;; Query time: 0 msec\n;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)\n;; WHEN: Wed Sep 28 22:46:22 EDT 2022\n;; MSG SIZE  rcvd: 73\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWithin the answer section you can see the IPv6 address set.\u003c/p\u003e\n\u003cp\u003eNext you can check if curl can grab the webpage via IPv6. Though importantly, you must be on an Internet connection with IPv6 support to test this. One webpage you can use to test is: \u003ca href=\"https://test-ipv6.com/\"\u003ehttps://test-ipv6.com/\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eAn example of using \u003ccode\u003ecurl\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e    -k \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t--header \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Host: brandonrozek.com\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e    -6 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e    https://\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e2600:3c03::f03c:93ff:fe89:8753\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e/\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSince we\u0026rsquo;re not accessing the website via the DNS record and instead the IPV6 address, the host name won\u0026rsquo;t match the SSL certificate. Hence we need to add \u003ccode\u003e-k\u003c/code\u003e for testing so that it can accept the \u0026ldquo;insecure\u0026rdquo; certificate. For a similar reason we need to specify the host header. My webserver runs multiple websites, so not specifying that will provide the default. The flag \u003ccode\u003e-6\u003c/code\u003e tells curl to use IPv6. Finally the URL goes at the end with the IPv6 address wrapped in square brackets.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eFor HTTP/2\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eKeyCDN provides a web \u003ca href=\"https://tools.keycdn.com/http2-test\"\u003etool\u003c/a\u003e to check, though luckily checking with curl on Linux is short.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl -I https://brandonrozek.com\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003e-I\u003c/code\u003e flag means only grab the headers.\u003c/p\u003e\n\u003cp\u003eThis results in:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eHTTP/2 200 \nserver: nginx\ndate: Thu, 29 Sep 2022 03:01:50 GMT\ncontent-type: text/html\ncontent-length: 7946\nlast-modified: Wed, 28 Sep 2022 23:00:01 GMT\nvary: Accept-Encoding\netag: \u0026#34;6334d1f1-1f0a\u0026#34;\naccept-ranges: bytes\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe first line will contain the string \u003ccode\u003eHTTP/2\u003c/code\u003e if your website supports it.\u003c/p\u003e\n\u003ch2 id=\"changes\"\u003eChanges\u003c/h2\u003e\n\u003cp\u003eInitially I did not have this setup, there were three edits that I needed to make on the webserver:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eEdits to the podman network\u003c/li\u003e\n\u003cli\u003eEdits to the docker-compose file\u003c/li\u003e\n\u003cli\u003eEdits to the Nginx configuratoin\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eStarting from the podman network. First lets check if IPv6 is enabled:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo podman network inspect network_name | grep ipv6\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf its enabled, then it will print out true as its value. If not, then we\u0026rsquo;ll need to recreate the network. First you\u0026rsquo;ll have to stop and remove all containers using that network. Then remove the network.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s an example to create a new network:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo podman network create \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t--ipv6 --subnet 2022:db8:abc1::/64 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t--subnet 10.11.12.0/24 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\tnetwork_name\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn the docker-compose you likely have a section where you bind ports 80 and 443 in the Nginx container to the host:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eports\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e - \u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e - \u003cspan style=\"color:#ae81ff\"\u003e443\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e443\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow we need to add the TCPv6 bindings:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eports\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e - \u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e - \u003cspan style=\"color:#ae81ff\"\u003e443\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e443\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e - :::\u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e - :::\u003cspan style=\"color:#ae81ff\"\u003e443\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e443\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen restart the nginx container.\u003c/p\u003e\n\u003cp\u003eTo see if the ports  are open on the host (ex: 443), we can use \u003ccode\u003enetstat\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo netstat -anlp | grep \u003cspan style=\"color:#ae81ff\"\u003e443\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis should return:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003etcp   0  0  0.0.0.0:443  0.0.0.0:*  LISTEN  51911/conmon        \ntcp6  0  0  :::443       :::*       LISTEN  51911/conmon\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWhere \u003ccode\u003econmon\u003c/code\u003e is the connection monitor which passes traffic to the podman container.\u003c/p\u003e\n\u003cp\u003eFinally changes need to be made within the nginx configuration to use TCPv6 and HTTP/2.\u003c/p\u003e\n\u003cp\u003eWithin the server directives:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eserver\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e[::]:80\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e# ....\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eserver\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e443\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003essl\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e[::]:443\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003essl\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ehttp2\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eon\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# ...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen restart the \u003ccode\u003enginx\u003c/code\u003e container and check \u003ccode\u003enetstat\u003c/code\u003e within the contaner.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo docker-compose exec nginx_container_name sh -c \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;netstat -anlp | grep 443\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis should output something similar to:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003etcp  0  0  0.0.0.0:443  0.0.0.0:*  LISTEN  172/nginx\ntcp  0  0  :::443       :::*       LISTEN  172/nginx\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThere you have it! Now your website should be TCPv6 and HTTP/2 compatible. Feel free to get in touch if you have any questions.\u003c/p\u003e\n",
    "date_published": "2022.09.28",
    "tags": ["Networking","Containers","Nginx"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "ac419c65c080"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/trim-video-ffmpeg/",
    "url": "https://brandonrozek.com/blog/trim-video-ffmpeg/",
    "title": "How to trim a video using FFMPEG",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eRecently I came across a video that I wanted to split up into multiple files. Given my love for \u003ccode\u003effmpeg\u003c/code\u003e the video/audio swiss army knife, I knew there had to be a solution for cutting a video on the terminal. Luckily on \u003ca href=\"https://askubuntu.com/a/56044\"\u003eAskUbuntu\u003c/a\u003e, Luis Alvarado provides a command snippet. This post will go into slightly more detail on the flags used in the command\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003effmpeg -ss 00:00:00 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t-t 00:30:00 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t-i input.mp4 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t-vcodec copy \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t-acodec copy \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\toutput.mp4\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eCommand\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-ss\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003ePosition within the input file. Most file formats do not allow exact seeking, so ffmpeg will pick the closest seek point. Format is in \u003cem\u003etime duration\u003c/em\u003e notation.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-t\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eLimits the duration of data read from the input file.  Format is in \u003cem\u003etime duration\u003c/em\u003e notation.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-vcodec\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eFormat used to transcribe the video. Use \u003ccode\u003ecopy\u003c/code\u003e to not transcribe to a different format.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-acodec\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eFormat used to transcribe the audio. Use \u003ccode\u003ecopy\u003c/code\u003e to not transcribe to a different format.\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eTime duration notation follows the format \u003ccode\u003e\u0026lt;HH\u0026gt;:\u0026lt;MM\u0026gt;:\u0026lt;SS\u0026gt;\u003c/code\u003e where \u003ccode\u003eHH\u003c/code\u003e is the number of hours, \u003ccode\u003eMM\u003c/code\u003e is the number of minutes, and \u003ccode\u003eSS\u003c/code\u003e is the number of seconds.\u003c/p\u003e\n\u003cp\u003eFor this command in general, the output video length would be the same time duration as specified in the \u003ccode\u003e-t\u003c/code\u003e flag.\u003c/p\u003e\n",
    "date_published": "2022.09.28",
    "tags": ["Audio-Video"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "dd83a32af440"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/website-status-checking/",
    "url": "https://brandonrozek.com/blog/website-status-checking/",
    "title": "Website Status Checking",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eHow do I know when my website goes up or down? Recently, I started using a service from \u003ca href=\"https://updown.io\"\u003eupdown.io\u003c/a\u003e which gives me a status page and notifies me when it goes down.\u003c/p\u003e\n\u003cp\u003eQ1: Why are you not self-hosting this via say \u003ca href=\"https://github.com/dgtlmoon/changedetection.io\"\u003echangedetection.io\u003c/a\u003e?\u003c/p\u003e\n\u003cp\u003eFirst, I\u0026rsquo;ve never been a big fan of setting up email alerting. It\u0026rsquo;s not because of its technical difficulty, but mostly since I\u0026rsquo;m lazy. I don\u0026rsquo;t want to have to setup an email address with SMTP access for applications to use. Similar laziness prevents me from creating API tokens for the various chat applications.\u003c/p\u003e\n\u003cp\u003eThere\u0026rsquo;s also the issue of how do I know when my monitoring system is down? I want this to be as hands-off as possible, so regularly checking my self-hosted service to see if its working properly is a no-go.\u003c/p\u003e\n\u003cp\u003eQ2: Okay, but surely you can download an application locally to check constantly instead?\u003c/p\u003e\n\u003cp\u003eIndeed! I admit, I haven\u0026rsquo;t fully pursued this option. In regards to mobile, I\u0026rsquo;m hesitant to download any app that\u0026rsquo;s meant to be consistently on. There\u0026rsquo;s the practical reason of battery performance, as mobile operating systems often freeze applications in its battery optimization. Also, I\u0026rsquo;ve never been a fan of having persistent notifications.\u003c/p\u003e\n\u003cp\u003eIn my quick search, I haven\u0026rsquo;t found an uptime monitor on Linux that doesn\u0026rsquo;t require a whole web application. I was thinking maybe there would a gnome or KDE widget. Ideally it would be a systemd timer or CRON job that would use the notification API when it notices your website is down.\u003c/p\u003e\n\u003cp\u003eQ3: Can I see your uptime?\u003c/p\u003e\n\u003cp\u003eSure! It\u0026rsquo;s shown at \u003ca href=\"https://status.brandonrozek.com\"\u003estatus.brandonrozek.com\u003c/a\u003e. Luckily their service allows for CNAMEs so that I can use my domain name. The CNAME points to page.updown.io and a TXT record on my domain then tells updown to point it to \u003ca href=\"https://updown.io/lbxi\"\u003ehttps://updown.io/lbxi\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eQ4: Anything else?\u003c/p\u003e\n\u003cp\u003eIn addition to a simple UP/DOWN metric. The service checks for TCPv4, TCPv6, and HTTP2 capabilities. I note this because I didn\u0026rsquo;t even notice that I wasn\u0026rsquo;t supporting TCPv6. In a future post, I\u0026rsquo;ll document the changes I made to my setup to enable it.\u003c/p\u003e\n\u003cp\u003eAlso, the service is quite affordable. To check one website every 30 minutes amounts to 2 cents a month. If you want to check it every 15 seconds, then it goes up to a little over $2/month.\u003c/p\u003e\n\u003cp\u003eFeel free to get in touch with how you monitor your personal website. I\u0026rsquo;m curious to see how other people are approaching it.\u003c/p\u003e\n",
    "date_published": "2022.09.28",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "4655285d688f"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/latex-footnote-no-count/",
    "url": "https://brandonrozek.com/blog/latex-footnote-no-count/",
    "title": "Quick LaTex: Footnotes with no Counter",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLet\u0026rsquo;s say there\u0026rsquo;s a scenario where you want to have a footnote, but you don\u0026rsquo;t want a counter associated with it. In order to stay consistent with the document style, the solution should use \u003ccode\u003e\\footnote\u003c/code\u003e within its implementation.\u003c/p\u003e\n\u003cp\u003eThe solution: Define \u003ccode\u003e\\blfootnote{text}\u003c/code\u003e in the preamble.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\newcommand\\blfootnote\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[1]\u003c/span\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003e\\begingroup\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003e\\renewcommand\\thefootnote\u003c/span\u003e{}\u003cspan style=\"color:#66d9ef\"\u003e\\footnote\u003c/span\u003e{#1}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003e\\addtocounter\u003c/span\u003e{footnote}{-1}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003e\\endgroup\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis makes use of the footnote command while also re-adjusting the footnote counts so that our variant doesn\u0026rsquo;t increase it. We can then use this command within the document. Here\u0026rsquo;s a beamer example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{frame}{Some Topic}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tThis is where I explain some topic.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e\\blfootnote\u003c/span\u003e{Numberless Footnote}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{frame}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eComplete Minimal Example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\documentclass\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[aspectratio=169]\u003c/span\u003e{beamer}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usepackage\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[utf8]\u003c/span\u003e{inputenc}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usetheme\u003c/span\u003e{Copenhagen}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\newcommand\\blfootnote\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[1]\u003c/span\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003e\\begingroup\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003e\\renewcommand\\thefootnote\u003c/span\u003e{}\u003cspan style=\"color:#66d9ef\"\u003e\\footnote\u003c/span\u003e{#1}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003e\\addtocounter\u003c/span\u003e{footnote}{-1}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003e\\endgroup\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{document}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{frame}{Some Topic}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tThis is where I explain some topic.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e\\blfootnote\u003c/span\u003e{Numberless Footnote}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{frame}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{document}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.09.23",
    "tags": ["LaTex"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "d0a88900c580"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/gps-exif-data-from-photos-decimal-format/",
    "url": "https://brandonrozek.com/blog/gps-exif-data-from-photos-decimal-format/",
    "title": "Retreiving GPS data in decimal format from EXIF data in photos",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eFor a new feature that I\u0026rsquo;m cooking up for my website, I need to grab the GPS information from the EXIF data stored in my images. Luckily, \u003ccode\u003eimagemagick\u003c/code\u003e\ncomes to our rescue.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eidentify -verbose $IMAGE_FILE | grep GPS\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will return something like the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eexif:GPSAltitude: 1250/100\nexif:GPSAltitudeRef: .\nexif:GPSDateStamp: 2022:06:12\nexif:GPSImgDirection: 137/1\nexif:GPSImgDirectionRef: M\nexif:GPSInfo: 1004\nexif:GPSLatitude: 40/1, 50/1, 1815/100\nexif:GPSLatitudeRef: N\nexif:GPSLongitude: 73/1, 53/1, 3625/100\nexif:GPSLongitudeRef: W\nexif:GPSTimeStamp: 17/1, 32/1, 30/1\nexif:GPSVersionID: ....\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTo request a specific field, for example Latitude:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eidentify -format \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;%[EXIF:GPSLatitude]\\n\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$IMAGE_FILE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAs with the verbose flag, it will return the information in degrees format\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e40/1, 50/1, 1815/100\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe following bash function will take the degrees format and convert\nit to the more common decmial format:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDegreesToDecimal\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        L0\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | cut -d \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;,\u0026#34;\u003c/span\u003e -f 1\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        L1\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | cut -d \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;,\u0026#34;\u003c/span\u003e -f 2\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        L2\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | cut -d \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;,\u0026#34;\u003c/span\u003e -f 3\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;scale=6;\u003c/span\u003e$L0\u003cspan style=\"color:#e6db74\"\u003e + \u003c/span\u003e$L1\u003cspan style=\"color:#e6db74\"\u003e/60 + \u003c/span\u003e$L2\u003cspan style=\"color:#e6db74\"\u003e/3600\u0026#34;\u003c/span\u003e | bc\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLAT\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eidentify -format \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;%[EXIF:GPSLatitude]\\n\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$IMAGE_FILE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLAT_DEC\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eDegreesToDecimal \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$LAT\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$LAT_DEC\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003ewill output:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e40.838374\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe can then package this into a script which will output the latitude, longitude, and altitude (m) of an image in decmial format.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/sh\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o errexit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o nounset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o pipefail\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eshow_usage\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Usage: getLLA.sh [imagefile]\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check argument count\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$#\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -ne \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    show_usage\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check that relevant command exist\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e ! command -v identify \u0026gt; /dev/null; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Command identify from imagemagick not found. Exiting...\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eIMAGE_FILE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLAT\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eidentify -format \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;%[EXIF:GPSLatitude]\\n\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$IMAGE_FILE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLAT_DIR\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eidentify -format \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;%[EXIF:GPSLatitudeRef]\\n\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$IMAGE_FILE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLON\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eidentify -format \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;%[EXIF:GPSLongitude]\\n\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$IMAGE_FILE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLON_DIR\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eidentify -format \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;%[EXIF:GPSLongitudeRef]\\n\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$IMAGE_FILE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eALT\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eidentify -format \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;%[EXIF:GPSAltitude]\\n\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$IMAGE_FILE\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDegreesToDecimal\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        L0\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | cut -d \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;,\u0026#34;\u003c/span\u003e -f 1\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        L1\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | cut -d \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;,\u0026#34;\u003c/span\u003e -f 2\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        L2\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | cut -d \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;,\u0026#34;\u003c/span\u003e -f 3\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;scale=6;\u003c/span\u003e$L0\u003cspan style=\"color:#e6db74\"\u003e + \u003c/span\u003e$L1\u003cspan style=\"color:#e6db74\"\u003e/60 + \u003c/span\u003e$L2\u003cspan style=\"color:#e6db74\"\u003e/3600\u0026#34;\u003c/span\u003e | bc\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLAT_DEC\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eDegreesToDecimal \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$LAT\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLON_DEC\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eDegreesToDecimal \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$LON\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eALT_DEC\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;scale=6;\u003c/span\u003e$ALT\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | bc\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLAT_PREFIX\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e $LAT_DIR \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;S\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;-\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLON_PREFIX\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e $LON_DIR \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;W\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;-\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$LAT_PREFIX$LAT_DEC\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$LON_PREFIX$LON_DEC\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$ALT_DEC\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.06.19",
    "tags": ["GPS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "45193055c22d"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/conditional-assignment-bash/",
    "url": "https://brandonrozek.com/blog/conditional-assignment-bash/",
    "title": "Conditional Assignment in Bash",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eMany programming languages include an quick way to perform a\nconditional assignment. That is, assigning a variable with a value\nbased on some condition. Normally this is done through a ternary\noperator. For example, here is how to write it in Javascript\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eage\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e16\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eageType\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eage\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e18\u003c/span\u003e) \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Adult\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Child\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe variable \u003ccode\u003eageType\u003c/code\u003e is dependent upon the value of \u003ccode\u003eage\u003c/code\u003e. If it is above 18 then \u003ccode\u003eageType = \u0026quot;Adult\u0026quot;\u003c/code\u003e otherwise \u003ccode\u003eageType = \u0026quot;Child\u0026quot;\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eA more verbose way of accomplishing the same thing is the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eage\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e18\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eageType\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Adult\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e} \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eageType\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Child\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHow do we do conditional assignment in Bash? One way is to make use of subshells and echoing out the values.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAGE_TYPE\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e $AGE -gt \u003cspan style=\"color:#ae81ff\"\u003e18\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Adult\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Child\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eA common programming feature called \u003cem\u003eshort-circuiting\u003c/em\u003e makes it\nso that if the first condition (\u003ccode\u003e[ $AGE -gt 18 ]\u003c/code\u003e) is false, then it\nwill skip the right side of the AND (\u003ccode\u003e\u0026amp;\u0026amp;\u003c/code\u003e) expression. This is because\n\u003ccode\u003eFalse \u0026amp;\u0026amp; True\u003c/code\u003e is always \u003ccode\u003eFalse\u003c/code\u003e. However, \u003ccode\u003eFalse || True\u003c/code\u003e is equal\nto \u003ccode\u003eTrue\u003c/code\u003e, so the language needs to evaluate the right part of an\nOR (\u003ccode\u003e||\u003c/code\u003e) expression.\u003c/p\u003e\n",
    "date_published": "2022.06.19",
    "tags": ["Bash"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "8217bfd3af16"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/website-visible-on-the-fediverse/",
    "url": "https://brandonrozek.com/blog/website-visible-on-the-fediverse/",
    "title": "Having your Website Visible on the Fediverse",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://www.w3.org/TR/activitypub/\"\u003eActivityPub\u003c/a\u003e is the backbone\nof the \u003ca href=\"https://en.wikipedia.org/wiki/Fediverse\"\u003eFediverse\u003c/a\u003e.\nImplementing this protocol will allow one to interact with users on\n\u003ca href=\"https://joinmastodon.org/\"\u003eMastodon\u003c/a\u003e,\n\u003ca href=\"https://pixelfed.org/\"\u003ePixelfed\u003c/a\u003e,\n\u003ca href=\"https://friendi.ca/\"\u003eFriendica\u003c/a\u003e,\nand \u003ca href=\"http://fediverse.party/\"\u003eothers\u003c/a\u003e.\nUnfortunately for a static website, this protocol uses a\npublish-subscribe pattern. A service would need to be written\nin order to handle subscribers, pushing messages of published items,\nas well as receiving mentions.\nAt the current state of my website, I am uninterested in creating\nsuch a service. However, I do wish to document the parts\nof the ActivityPub protocol I was able to implement so far\nusing only a static website.\nThis post extends off of\n\u003ca href=\"https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/\"\u003eEugen Rochko\u0026rsquo;s work\u003c/a\u003e\nwhich I encourage you to check out.\u003c/p\u003e\n\u003ch2 id=\"webfinger\"\u003eWebfinger\u003c/h2\u003e\n\u003cp\u003eWhen someone looks up a handle on Mastodon, it performs a Webfinger\nlookup. For example if I am looking up the user \u003ccode\u003ebrozek@brandonrozek.com\u003c/code\u003e,\nthen the service will perform a GET request at\n\u003ccode\u003ehttps://brandonrozek.com/.well-known/webfinger?resource=acct:brozek@brandonrozek.com\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eA Webfinger request should return the handle of the user (the \u003ccode\u003esubject\u003c/code\u003e) as well as\na link to its \u003ccode\u003eactor\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;subject\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;acct:brozek@brandonrozek.com\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;links\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;rel\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;self\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;application/activity+json\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;href\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://brandonrozek.com/brozek.json\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow for a static site with one user, you can hardcode the Webfinger directly\nat \u003ccode\u003e.well-known/webfinger\u003c/code\u003e. The only issue is that many webservers determine the\nmime-type of a file by its extension. In this case, we\u0026rsquo;ll have to let the webserver\n(mine is nginx) know how to serve it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003elocation\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/.well-known/webfinger\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003edefault_type\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eapplication/jrd+json\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eadd_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eAccess-Control-Allow-Origin\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;*\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"actor\"\u003eActor\u003c/h2\u003e\n\u003cp\u003eAn actor defines how to interact with a user as well as its metadata.\nThis file also needs to be served with the \u003ccode\u003eapplication/jrd+json\u003c/code\u003e header.\nBelow is an example of what I have on my website as well as how it looks\non Mastodon.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;@context\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://www.w3.org/ns/activitystreams\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://w3id.org/security/v1\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://brandonrozek.com/brozek.json\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Person\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;inbox\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://brandonrozek.com/inbox\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;outbox\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://granary.io/url?input=rss\u0026amp;output=as2\u0026amp;url=http%3A%2F%2Fbrandonrozek.com%2Fblog%2Findex.xml\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;preferredUsername\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;brozek\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon Rozek\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;summary\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026lt;p\u0026gt;Writings from \u0026lt;a href=\u0026#39;https://brandonrozek.com\u0026#39;\u0026gt;https://brandonrozek.com\u0026lt;/p\u0026gt;\u0026lt;/a\u0026gt;\u0026lt;p\u0026gt;Social profile \u0026lt;a href=\u0026#39;https://fosstodon.org/@brozek\u0026#39;\u0026gt;@brozek@fosstodon.org\u0026lt;/a\u0026gt;\u0026lt;/p\u0026gt;\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;url\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://brandonrozek.com\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;manuallyApprovesFollowers\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;discoverable\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;published\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2022-05-17T00:00:00Z\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;icon\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Image\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;mediaType\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;image/jpeg\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;url\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://brandonrozek.com/img/avatar.jpg\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;publicKey\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://brandonrozek.com/brozek.json#main-key\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;owner\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://brandonrozek.com.com/brozek.json\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#f92672\"\u003e\u0026#34;publicKeyPem\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/202206121902.png\" alt=\"Screenshot of brozek@brandonrozek.com profile on Mastodon\"\u003e\u003c/p\u003e\n\u003ch2 id=\"limitations-and-conclusion\"\u003eLimitations and Conclusion\u003c/h2\u003e\n\u003cp\u003eNotice in the previous screenshot that it says \u0026ldquo;Cancel Follow Request\u0026rdquo;.\nThis is because my static website is not equipped to handle a follow request.\nStandard ActivityPub servers keep an inventory of all the users that follow\ntheir users and who each user on the server is following.\u003c/p\u003e\n\u003cp\u003eThe outbox defines items that the user has published.\nI have it pointed to an activity JSON feed generated directly\nfrom my RSS using granary.io.\nMastodon correctly displays the number of blog posts I have,\nbut sadly it does not show any of them. I\u0026rsquo;m not entirely\nsure why, though my hypothesis is that the posts need to be pushed\nto the Mastodon server when its created. It could also be that the\nactor isn\u0026rsquo;t defined properly in the activity JSON feed. If you happen\nto know please get in touch.\u003c/p\u003e\n",
    "date_published": "2022.06.12",
    "tags": ["Mastodon"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "3c4fe896f3fb"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/git-pushing-multiple-remotes/",
    "url": "https://brandonrozek.com/blog/git-pushing-multiple-remotes/",
    "title": "Git Pushing to Multiple Remotes",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eGit\u0026rsquo;s greatest strength is its first-class support for decentralization.\nSadly, GitHub has taken over as the sole location to store code for many\npeople.\u003c/p\u003e\n\u003cp\u003eIn order to not \u003cem\u003eput all my eggs into one basket\u003c/em\u003e, I want to utilize multiple\ncode hosting websites to store my public repositories.\nThis is not only for the GitHub zombie apocolypse scenario, but local\noutages do in fact happen and its nice to have a backup.\u003c/p\u003e\n\u003cp\u003eIdeally this backup would not come at a cost of convinience. In fact,\nwe can edit the remotes of our git repository so that a simple\n\u003ccode\u003egit push\u003c/code\u003e updates all of our remotes.\u003c/p\u003e\n\u003cp\u003eThe following is an example from my website.\nWithin your repository, use the command \u003ccode\u003egit config -e\u003c/code\u003e to open an editor with your\nrepository\u0026rsquo;s git conifguration. Then edit the origin block to be configured\nwith multiple push-urls.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[remote \u0026#34;origin\u0026#34;]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003egit@github.com:Brandon-Rozek/website.git \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        fetch = +refs/heads/*:refs/remotes/origin/*\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        pushurl = git@github.com:Brandon-Rozek/website.git\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        pushurl = git@git.sr.ht:~brandonrozek/website\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAfter this, typing \u003ccode\u003egit push\u003c/code\u003e every pushurl you have configured.\nFor me, it updates both my \u003ca href=\"https://github.com/brandon-rozek/website\"\u003eGitHub repository\u003c/a\u003e\nas well as my \u003ca href=\"https://sr.ht/~brandonrozek/website/\"\u003eSourceHut repository\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eI only recently started using \u003ca href=\"https://sr.ht/\"\u003eSourceHut\u003c/a\u003e.\nIt\u0026rsquo;s designed by \u003ca href=\"https://drewdevault.com/\"\u003eDrew Devault\u003c/a\u003e\nand others to feature the original usage of git, via email.\nThis method is still in use by the Linux kernel development team.\nI\u0026rsquo;m excited to try it out and hopefully write some future posts on this concept.\u003c/p\u003e\n",
    "date_published": "2022.06.02",
    "tags": ["Git"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "41533d2710fd"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/displaying-hikes-with-gpxstudio/",
    "url": "https://brandonrozek.com/blog/displaying-hikes-with-gpxstudio/",
    "title": "Displaying Hikes with gpx.studio",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eAs the weather gets warmer, I am starting to go on more hikes. Several people on their websites share a Strava embed which highlights a path taken during their workout. I believe as a community this has great potential for sharing our favorite hiking paths. I don\u0026rsquo;t, however, want to rely on Strava to host my GPS data. Instead, we will showcase how to accomplish the same effect but with open technologies.\u003c/p\u003e\n\u003cp\u003eAt the end of this post, we will achieve in creating an embed like the following:\u003c/p\u003e\n\n\n\n\n\n\n\n\u003ciframe src=\"https://gpx.studio/?state=%7B%22urls%22%3A%5B%22https%3A%2F%2Fbrandonrozek.com%2Fdata%2Ftracks%2F2022-05-18-Burden-Pond-Preserve.gpx%22%5D%7D\u0026embed\u0026distance\"\n        width=\"100%\"\n        height=\"500\"\n        frameborder=\"0\"\n        allowfullscreen\u003e\n            \u003cp\u003e\u003ca href=\"https://gpx.studio/?state=%7B%22urls%22%3A%5B%22https%3A%2F%2Fbrandonrozek.com%2Fdata%2Ftracks%2F2022-05-18-Burden-Pond-Preserve.gpx%22%5D%7D\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c/iframe\u003e\n\n\u003cp\u003eTo get there, we will talk about:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"#recording-the-walk\"\u003eRecording the Walk\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#grabbing-an-embed\"\u003eGrabbing an embed\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#shortcodes-for-website\"\u003eShortcodes for website\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#conclusion\"\u003eConclusion\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"recording-the-walk\"\u003eRecording the Walk\u003c/h2\u003e\n\u003cp\u003eMy first requirement is that the route is recorded in an open source format. For this I chose GPX otherwise known as the GPS Exchange format. There are three types of messages that a GPX file can contain:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eWaypoint\u003c/strong\u003e: A point of interested denoted by a latitude/longitude/altitude (LLA) and a time with a small note describing it.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eRoute\u003c/strong\u003e: An ordered list of LLA and time points used to denote a navigation from a start to end location.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eTrack\u003c/strong\u003e: An ordered list of frequent LLA and time points denoting a GPS feed.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003ePopular applications such as AllTrails and Strava allow you to export your activities as GPX files. These will generally contain the track information. A cool open source alternative application called \u003ca href=\"https://wiki.openstreetmap.org/wiki/OSMTracker_(Android)\"\u003eOSMTracker\u003c/a\u003e records track information as well as enabling an easy way to create waypoints.\u003c/p\u003e\n\u003ch2 id=\"grabbing-an-embed\"\u003eGrabbing an embed\u003c/h2\u003e\n\u003cp\u003eTo create the embed we will use a project called \u003ca href=\"https://gpx.studio\"\u003egpx.studio\u003c/a\u003e. It\u0026rsquo;s a great \u003ca href=\"https://github.com/gpxstudio/gpxstudio.github.io\"\u003eopen source\u003c/a\u003e project created by vcoppe on GitHub and built on top of \u003ca href=\"https://www.mapbox.com/\"\u003eMapbox\u003c/a\u003e and \u003ca href=\"https://www.openstreetmap.org/\"\u003eOpenStreetMap\u003c/a\u003e. If you end up using this application, consider providing a \u003ca href=\"https://ko-fi.com/gpxstudio\"\u003edonation\u003c/a\u003e to their developer to cover for some of the hosting and API costs.\nOur next step is to upload the GPX file we recorded from our exercise onto our website and configure our webserver so that gpx.studio can access the file. I store my hikes in \u003ccode\u003edata/tracks\u003c/code\u003e and created the following Nginx rule:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Allow any application access\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003elocation\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/data/tracks\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eadd_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eAccess-Control-Allow-Origin\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;*\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNext, go to the \u003ca href=\"https://gpx.studio/about.html#embed\"\u003egpx.studio about page\u003c/a\u003e to enter in the URL in the embed section. It will then output the following HTML code to add to your website\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003eiframe\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003esrc\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://gpx.studio/?state=%7B%22urls%22:%5B%22https%3A%2F%2Fbrandonrozek.com%2Fdata%2Ftracks%2F2022-05-18-Burden-Pond-Preserve.gpx%22%5D%7D\u0026amp;embed\u0026amp;distance\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003ewidth\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;100%\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003eheight\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;500\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003eframeborder\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;0\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003eallowfullscreen\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \t\u0026lt;\u003cspan style=\"color:#f92672\"\u003ep\u003c/span\u003e\u0026gt;\u0026lt;\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehref\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://gpx.studio/?state=%7B%22urls%22:%5B%22https%3A%2F%2Fbrandonrozek.com%2Fdata%2Ftracks%2F2022-05-18-Burden-Pond-Preserve.gpx%22%5D%7D\u0026#34;\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ep\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;/\u003cspan style=\"color:#f92672\"\u003eiframe\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"shortcodes-for-website\"\u003eShortcodes for website\u003c/h2\u003e\n\u003cp\u003eNotice that the URL structure follows a prescribed format. This means we can create a Hugo shortcode to  generate these iFrames for us! The embed shown earlier on this page was created using the following shortcode command:\u003c/p\u003e\n\u003cp\u003e{{\u0026lt; displayGPX \u0026ldquo;2022-05-18-Burden-Pond-Preserve.gpx\u0026rdquo; \u0026gt;}}\u003c/p\u003e\n\u003cp\u003eSo what\u0026rsquo;s the structure of the URLs? If we perform an URL decode on the IFrame URL we get the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ehttps://gpx.studio/?\n\tstate={\u0026#34;urls\u0026#34;:[\u0026#34;https://brandonrozek.com/data/tracks/2022-05-18-Burden-Pond-Preserve.gpx\u0026#34;]}\n\t\u0026amp;embed\u0026amp;distance\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe can then replace the URL in the array with the one that you\u0026rsquo;ll use for your website. Since I\u0026rsquo;m sticking all my GPX files under \u003ca href=\"https://brandonrozek.com/data/tracks\"\u003ehttps://brandonrozek.com/data/tracks\u003c/a\u003e, I only have to pass the filename into the shortcode which is stored under \u003ccode\u003elayouts/shortcodes/displayGPX.html\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u0026lt;!-- Grab the filename and append it to the base url --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ $url := printf \u0026#34;%sdata/tracks/%s\u0026#34; .Site.BaseURL (.Get 0) }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u0026lt;!-- Make the state={...} URL friendly --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ $url_state := querify \u0026#34;state\u0026#34; (printf \u0026#34;{\\\u0026#34;urls\\\u0026#34;:[\\\u0026#34;%s\\\u0026#34;]}\u0026#34; $url) }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u0026lt;!-- Create the Iframe and A tag URLs --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ $iframe_url := printf \u0026#34;https://gpx.studio/?%s\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u0026amp;\u003c/span\u003eembed\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u0026amp;\u003c/span\u003edistance\u0026#34; $url_state }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ $a_url := printf \u0026#34;https://gpx.studio/?%s\u0026#34; $url_state }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u0026lt;!-- Output the IFrame --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003eiframe\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{{\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003esrc\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e%q\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e$\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eiframe_url\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esafeHTMLAttr\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003ewidth\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;100%\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003eheight\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;500\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003eframeborder\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;0\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003eallowfullscreen\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u0026lt;\u003cspan style=\"color:#f92672\"\u003ep\u003c/span\u003e\u0026gt;\u0026lt;\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{{\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eprintf\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003ehref\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e%q\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e$\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003ea_url\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e|\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esafeHTMLAttr\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}}\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ep\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;/\u003cspan style=\"color:#f92672\"\u003eiframe\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eWith this we created a cool visual representation of GPX data on our own website built on top of open technologies. Since our routes are stored in GPX files, if gpx.studio was to go down, then we can likely find an alternative to plot the file for us. Gpx.studio is also built on top of OpenStreetMap, a great  long-standing community driven mapping project.\u003c/p\u003e\n",
    "date_published": "2022.05.23",
    "tags": ["Hugo","GPS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "ac442c9cf7fc"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/archiving-toots/",
    "url": "https://brandonrozek.com/blog/archiving-toots/",
    "title": "Archiving Toots",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn the spirit of \u003ca href=\"/blog/why-i-pesos-from-mastodon/\"\u003esyndicating Mastodon toots\u003c/a\u003e\nto my own site, I wrote a Python script that turns toots into Hugo markdown\nfiles.\u003c/p\u003e\n\u003cp\u003eIn this post we\u0026rsquo;ll go over:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"#mastodon-api\"\u003eMastodon API\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#reformatting-toot\"\u003eReformatting Toot\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#creating-the-markdown-files\"\u003eCreating the Markdown files\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#conclusion\"\u003eConclusion\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"mastodon-api\"\u003eMastodon API\u003c/h2\u003e\n\u003cp\u003eBefore we can retrieve our toots, we need to know what user id of our account.\nJames Cahill wrote a very clean \u003ca href=\"https://prouser123.me/mastodon-userid-lookup/\"\u003eweb tool\u003c/a\u003e\nto grab your user id. For the sake of example, we\u0026rsquo;ll use mine which\nis 108219415927856966.\u003c/p\u003e\n\u003cp\u003eTo grab the statuses, we then need to access the following URL:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ehttps://SERVER/api/v1/accounts/USERID\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFor my specific user:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ehttps://fosstodon.org/api/v1/accounts/108219415927856966\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eBy default, this will return 20 statuses in an array.\nTo see how to parse each individual status, check out my\npost on \u003ca href=\"/blog/displaying-a-toot-hugo/\"\u003edisplaying a single toot\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eYou can use the limit parameter to set how many statuses you wish to see.\nThe maximum number you can set it to is 40.\u003c/p\u003e\n\u003cp\u003eIn order to see more than the last 40 toots, there is another\nparameter that we can use called \u003ccode\u003emax_id\u003c/code\u003e which will specify the maximum\ntoot id to respond with.\nYou can then use this parameter to grab all your toots, by\nfollowing the following algorithm:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eMake an initial query to the API\u003c/li\u003e\n\u003cli\u003eFind the smallest toot id in the response\u003c/li\u003e\n\u003cli\u003eWhile the response is not empty\n\u003cul\u003e\n\u003cli\u003eSend a query with the \u003ccode\u003emax_id\u003c/code\u003e set to the smallest toot id known\u003c/li\u003e\n\u003cli\u003eUpdate the smallest toot id known\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eTogether with \u003ccode\u003elimit\u003c/code\u003e and \u003ccode\u003emax_id\u003c/code\u003e we can grab any specified number of toots.\nHere\u0026rsquo;s the psuedocode for that:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e _ \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e range(math\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eceil(RETRIEVE_NUM_TOOTS \u003cspan style=\"color:#f92672\"\u003e//\u003c/span\u003e MAX_TOOTS_PER_QUERY)):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    url \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ef\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{\u003c/span\u003eSERVER\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/api/v1/accounts/\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{\u003c/span\u003eUID\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/statuses\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    url \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;?limit=40\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e limit_param \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e40\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ef\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;?limit=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{\u003c/span\u003elimit_param\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    url \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026amp;max_id=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{max_id}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e max_id \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    response \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e query(url)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e len(resposne) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ebreak\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Process response...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"reformatting-toot\"\u003eReformatting Toot\u003c/h2\u003e\n\u003cp\u003eRather than storing the JSON of the toot verbatim, I do make some changes\nto it for the following reasons:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eBy default every toot has a lot of account information, this is an issue because\nif my number of followers update, then I need to update all my toots.\u003c/li\u003e\n\u003cli\u003eHugo expects certain field names to exist. For example: date.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eI delete the following account information from my toot archive:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eLock status\u003c/li\u003e\n\u003cli\u003eBot\u003c/li\u003e\n\u003cli\u003eDiscoverable\u003c/li\u003e\n\u003cli\u003eGroup\u003c/li\u003e\n\u003cli\u003eCreated_at\u003c/li\u003e\n\u003cli\u003eNote\u003c/li\u003e\n\u003cli\u003eFollower count\u003c/li\u003e\n\u003cli\u003eFollowing count\u003c/li\u003e\n\u003cli\u003eStatus count\u003c/li\u003e\n\u003cli\u003eLast status at\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eI create a date field based on \u003ccode\u003ecreated_at\u003c/code\u003e.\nThe URL field in mastodon conflicts with Hugo,\nso I rename it to \u003ccode\u003esyndication\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"creating-the-markdown-files\"\u003eCreating the Markdown files\u003c/h2\u003e\n\u003cp\u003eFrom here I can construct the following\nmarkdown file based on this template:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e---\n{Toot JSON}\n---\nJSON.content\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eI save each toot in an individual markdown file under \u003ccode\u003econtent/toots\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eMy full \u003ca href=\"https://github.com/Brandon-Rozek/website-toots/blob/main/.scripts/refreshtoots.py\"\u003escript\u003c/a\u003e\nis on GitHub.\nThe script will let you know of any toot IDs that are created\nand/or updated. I then add these toots to Git for version control\njust like my posts.\u003c/p\u003e\n",
    "date_published": "2022.05.20",
    "tags": ["Hugo","Mastodon","Archive"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "c1b813e3dff0"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/displaying-a-toot-hugo/",
    "url": "https://brandonrozek.com/blog/displaying-a-toot-hugo/",
    "title": "Displaying a Toot in Hugo",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eMastodon for me is a nice friendly place and I enjoy participating in that community. With that, I want to be able to share the great toots out there in my own website as well as keep an archive of all the toots I made. This post will go over the code I wrote in Hugo to display a single toot into a blog post.\u003c/p\u003e\n\u003cp\u003eExample toot:\u003c/p\u003e\n\n\n\n\n  \n    \n  \n\nw\n\n\n\n\n\n\u003carticle class=\"toot h-entry\"\u003e\n    \u003cdiv rel=\"author\" class=\"h-card p-author toot-avatar\"\u003e\n      \u003cimg class=\"u-photo\" width=50 src=\"https://cdn.fosstodon.org/accounts/avatars/108/219/415/927/856/966/original/c007afd0c6749859.png\"/\u003e\n      \u003cspan style=\"display: none;\"\u003eBrandon Rozek\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cp class=\"date\"\u003eTooted on \u003ctime class=\"dt-published\" datetime='2022-05-20T15:09:50.226Z'\u003eMay 20, 2022 15:09\u003c/time\u003e\u003c/p\u003e\n    \u003cdiv class=\"e-content p-name\"\u003e\n      \u003cp\u003e\u003cspan class=\"h-card\"\u003e\u003ca href=\"https://fosstodon.org/@ashshuota\" class=\"u-url mention\"\u003e@\u003cspan\u003eashshuota\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e \u003cspan class=\"h-card\"\u003e\u003ca href=\"https://fosstodon.org/@technicalissues\" class=\"u-url mention\"\u003e@\u003cspan\u003etechnicalissues\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e Sure I can write a post about my blog-toot setup! Give me a day or so to write it all out and I\u0026#39;ll ping you when it\u0026#39;s out :)\u003c/p\u003e\n      \n    \u003c/div\u003e\n    \u003ca class=\"u-url\" style=\"display: none\"\u003e\u003c/a\u003e\n    \n    \u003cdiv class=\"toot-social\"\u003e\n        \n        \n        \n        \n        \n        \n        \u003cspan\u003e\n          \u003cspan class='fas fa-share'\u003e\u003c/a\u003e\n          0\n        \u003c/span\u003e\n        \u003cspan\u003e\n          \u003cspan class='fas fa-retweet'\u003e\u003c/a\u003e\n          0\n        \u003c/span\u003e\n        \u003cspan\u003e\n          \u003cspan class='fas fa-star'\u003e\u003c/a\u003e\n          0\n        \u003c/span\u003e\n        \u003cspan\u003e| Source: \u003ca class=\"u-syndication\" href=\"https://fosstodon.org/@brozek/108334900197768307\"\u003ehttps://fosstodon.org/@brozek/108334900197768307\u003c/a\u003e\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/article\u003e\n  \n  \n  \n\n\u003cp\u003eIn this post, we will go over the following:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"#mastodon-api\"\u003eMastodon API\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#displaying-toot\"\u003eDisplaying Toot\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#hugo-shortcode\"\u003eHugo Shortcode\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#archiving-toots\"\u003eArchiving Toots\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#conclusion\"\u003eConclusion\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"mastodon-api\"\u003eMastodon API\u003c/h2\u003e\n\u003cp\u003eBefore jumping into the code right away, it is good to understand how to query data from Mastodon. The Mastodon API is so friendly that for public toots, you don\u0026rsquo;t even need an API key. The first thing we need to know is the toot id of the toot you are interested in. In my example above, the toot id is \u003ccode\u003e108334900197768307\u003c/code\u003e, you can get that by looking at the last string of numbers at the end of the toot URL. You can get the toot URL by clicking on \u0026ldquo;Copy link to post\u0026rdquo; in the three dot menu of the toot. Otherwise you can get it from the URL of the favorite and reblog button. Let\u0026rsquo;s say the URL of the toot is the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ehttps://fosstodon.org/@brozek/108334900197768307\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThen the API endpoint to get the JSON representation is at:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ehttps://fosstodon.org/api/v1/statuses/108334900197768307\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIn its most general form you\u0026rsquo;ll need to know the server the toot came from and its id:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ehttps://SERVER/api/v1/statuses/TOOTID\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIf you check out the example link, you\u0026rsquo;ll see that the JSON returned is of the form:\u003c/p\u003e\n\u003cdetails\u003e\n    \u003csummary\u003eExpand JSON:\u003c/summary\u003e\n    \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;108334900197768307\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;created_at\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2022-05-20T15:09:50.226Z\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;in_reply_to_id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;108331903834519586\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;in_reply_to_account_id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;108219415927856966\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;sensitive\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;spoiler_text\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;visibility\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;unlisted\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;language\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;en\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;uri\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://fosstodon.org/users/brozek/statuses/108334900197768307\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;url\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://fosstodon.org/@brozek/108334900197768307\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;replies_count\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;reblogs_count\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;favourites_count\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;edited_at\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;content\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;[Shortened for display purposes]\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;reblog\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;application\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;account\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;108219415927856966\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;username\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;brozek\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;acct\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;brozek\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;display_name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon Rozek\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;created_at\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2022-04-30T00:00:00.000Z\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;note\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;[Shortened for display purposes]\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;url\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://fosstodon.org/@brozek\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;avatar\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://cdn.fosstodon.org/accounts/avatars/.../c007afd0c6749859.png\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;header\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://fosstodon.org/headers/original/missing.png\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;followers_count\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e114\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;following_count\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e271\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;statuses_count\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e57\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;last_status_at\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2022-05-20\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;emojis\u0026#34;\u003c/span\u003e: [ ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;fields\u0026#34;\u003c/span\u003e: [ ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;media_attachments\u0026#34;\u003c/span\u003e: [],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;mentions\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;106627708559095741\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;username\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ashshuota\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;url\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://fosstodon.org/@ashshuota\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;acct\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ashshuota\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;107584265842142303\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;username\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;technicalissues\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;url\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://fosstodon.org/@technicalissues\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;acct\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;technicalissues\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;tags\u0026#34;\u003c/span\u003e: [],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;emojis\u0026#34;\u003c/span\u003e: [],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;card\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;poll\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/details\u003e\n\n\u003cp\u003eWow that\u0026rsquo;s a lot of information that comes with a single toot! For the purposes of my simple toot displayer I will only focus on a few of the fields:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eaccount.avatar\u003c/code\u003e: Avatar image of tooter\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ecreated_at\u003c/code\u003e:  Date toot was posted\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003emedia_attachments\u003c/code\u003e: Images within the toot (if any)\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eurl\u003c/code\u003e: URL of toot\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003econtent\u003c/code\u003e: Contents of toot\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eFor sake of simplicity, let\u0026rsquo;s ignore content warnings and boosts and revisit those in a future post.\u003c/p\u003e\n\u003ch2 id=\"displaying-toot\"\u003eDisplaying Toot\u003c/h2\u003e\n\u003cp\u003eThe following is the high level overview of our toots, complete with microformats2 metadata.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003earticle\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;toot h-entry\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erel\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;author\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;h-card p-author toot-avatar\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003eimg\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;u-photo\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ewidth\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e50\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esrc\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .account.avatar }}\u0026#34;\u003c/span\u003e/\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003estyle\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;display: none;\u0026#34;\u003c/span\u003e\u0026gt;{{ .account.display_name }}\u0026lt;/\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;/\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;\u003cspan style=\"color:#f92672\"\u003ep\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;date\u0026#34;\u003c/span\u003e\u0026gt;Tooted on\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003etime\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;dt-published\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edatetime\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;{{ .created_at }}\u0026#39;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      {{ dateFormat \u0026#34;January 2, 2006 15:04\u0026#34; (time .created_at) }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;/\u003cspan style=\"color:#f92672\"\u003etime\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;/\u003cspan style=\"color:#f92672\"\u003ep\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;e-content p-name\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {{ .content | safeHTML }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;toot-photos space-evenly\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#75715e\"\u003e\u0026lt;!-- TODO --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;/\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;/\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;toot-social\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e\u0026lt;!-- TODO --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;/\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;/\u003cspan style=\"color:#f92672\"\u003earticle\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe wrap the entire toot in an \u003ccode\u003eh-entry\u003c/code\u003e class. Then we begin by creating a div that contains the authors details such as their avatar. Afterwards, we display the date/time that the toot was made. Then we display the content of the toot and its images. Lastly, we display social interaction icons such as reply, favorite, and reblog.\u003c/p\u003e\n\u003cp\u003eTo display the images we range over the media_attachments array and grab any image URLs and their descriptions:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;toot-photos space-evenly\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ range .media_attachments}}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ if eq .type \u0026#34;image\u0026#34; }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;\u003cspan style=\"color:#f92672\"\u003eimg\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esrc\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .preview_url }}\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ealt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .description }}\u0026#34;\u003c/span\u003e/\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ end }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ end }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAt time time of post interaction URLs followed the upcoming pattern.\nThis has been removed since, and I have not yet found an alternative.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eReply URL -\u0026gt; \u003ccode\u003ehttps://SERVER/interact/TOOTID?type=reply\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eFavorite URL -\u0026gt; \u003ccode\u003ehttps://SERVER/interact/TOOTID?type=favourite\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eBoost URL -\u0026gt; \u003ccode\u003ehttps://SERVER/interact/TOOTID?type=reblog\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;toot-social\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  {{ $post_url := urls.Parse .url }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  {{ $status_id := index (last 1 (split $post_url.Path \u0026#34;/\u0026#34;)) 0 }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  {{ $social_url := printf \u0026#34;%s://%s/interact/%s\u0026#34; $post_url.Scheme $post_url.Host $status_id }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  {{ $reply_url := printf \u0026#34;%s?type=reply\u0026#34; $social_url }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  {{ $favorite_url := printf \u0026#34;%s?type=favourite\u0026#34; $social_url }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  {{ $boost_url := printf \u0026#34;%s?type=reblog\u0026#34; $social_url }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;fas fa-share\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehref\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ $reply_url }}\u0026#34;\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      {{ .replies_count }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;/\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;fas fa-retweet\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehref\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ $boost_url }}\u0026#34;\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      {{ .reblogs_count }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;/\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;fas fa-star\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehref\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ $favorite_url }}\u0026#34;\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      {{ .favourites_count }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;/\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;| Source: \u0026lt;\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;u-syndication\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehref\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .url }}\u0026#34;\u003c/span\u003e\u0026gt;{{ .url }}\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"hugo-shortcode\"\u003eHugo Shortcode\u003c/h2\u003e\n\u003cp\u003eWe can then stick this all into a shortcode so that I can easily embed a toot into a blog post!\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s go with the format \u003ccode\u003e{{ displayOnlineToot \u0026quot;https://fosstodon.org/@brozek/108334900197768307\u0026quot;}}\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eWe\u0026rsquo;ll have to add the following contents to \u003ccode\u003etheme/layouts/shortcodes/displayOnlineToot.html\u003c/code\u003e:\u003c/p\u003e\n\u003cdetails\u003e\n    \u003csummary\u003eShow code:\u003c/summary\u003e\n    \u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ $url := urls.Parse (.Get 0) }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ $status_id := index (last 1 (split $url.Path \u0026#34;/\u0026#34;)) 0 }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ $api_url := printf \u0026#34;%s://%s/api/v1/statuses/%s\u0026#34; $url.Scheme $url.Host $status_id }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ $dataJ := dict }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ $url := $api_url }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ with try (resources.GetRemote $url) }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  {{ with .Err }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {{ errorf \u0026#34;Unable to get remote resource %s: %s\u0026#34; $url . }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  {{ else with .Value }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {{ $dataJ = . | transform.Unmarshal }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  {{ else }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {{ errorf \u0026#34;Unable to get remote resource %s\u0026#34; $url }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  {{ end }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ end }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ with $dataJ }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ if ne .content \u0026#34;\u0026#34; }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003earticle\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;toot h-entry\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erel\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;author\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;h-card p-author toot-avatar\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u0026lt;\u003cspan style=\"color:#f92672\"\u003eimg\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;u-photo\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ewidth\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e50\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esrc\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .account.avatar }}\u0026#34;\u003c/span\u003e/\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u0026lt;\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003estyle\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;display: none;\u0026#34;\u003c/span\u003e\u0026gt;{{ .account.display_name }}\u0026lt;/\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;/\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003ep\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;date\u0026#34;\u003c/span\u003e\u0026gt;Tooted on \u0026lt;\u003cspan style=\"color:#f92672\"\u003etime\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;dt-published\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edatetime\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;{{ .created_at }}\u0026#39;\u003c/span\u003e\u0026gt;{{ dateFormat \u0026#34;January 2, 2006 15:04\u0026#34; (time .created_at) }}\u0026lt;/\u003cspan style=\"color:#f92672\"\u003etime\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ep\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;e-content p-name\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      {{ .content | safeHTML }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      {{ if gt (len .media_attachments) 0 }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u0026lt;\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;toot-photos space-evenly\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      {{ range .media_attachments}}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {{ if eq .type \u0026#34;image\u0026#34; }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u0026lt;\u003cspan style=\"color:#f92672\"\u003eimg\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esrc\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .preview_url }}\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ealt\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .description }}\u0026#34;\u003c/span\u003e/\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {{ end }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      {{ end }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u0026lt;/\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      {{ end }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;/\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;u-url\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003estyle\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;display: none\u0026#34;\u003c/span\u003e\u0026gt;{{ .Permalink }}\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {{ range .tags }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;p-category\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehref\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .url }}\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003estyle\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;display: none;\u0026#34;\u003c/span\u003e\u0026gt;{{ .name }}\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {{ end }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;toot-social\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {{ $post_url := urls.Parse .url }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {{ $status_id := index (last 1 (split $post_url.Path \u0026#34;/\u0026#34;)) 0 }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {{ $social_url := printf \u0026#34;%s://%s/interact/%s\u0026#34; $post_url.Scheme $post_url.Host $status_id }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {{ $reply_url := printf \u0026#34;%s?type=reply\u0026#34; $social_url }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {{ $favorite_url := printf \u0026#34;%s?type=favourite\u0026#34; $social_url }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {{ $boost_url := printf \u0026#34;%s?type=reblog\u0026#34; $social_url }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u0026lt;\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u0026lt;\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;fas fa-share\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehref\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ $reply_url }}\u0026#34;\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          {{ .replies_count }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u0026lt;/\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u0026lt;\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u0026lt;\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;fas fa-retweet\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehref\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ $boost_url }}\u0026#34;\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          {{ .reblogs_count }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u0026lt;/\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u0026lt;\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u0026lt;\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;fas fa-star\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehref\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ $favorite_url }}\u0026#34;\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          {{ .favourites_count }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u0026lt;/\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u0026lt;\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;| Source: \u0026lt;\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;u-syndication\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehref\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .url }}\u0026#34;\u003c/span\u003e\u0026gt;{{ .url }}\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003espan\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;/\u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;/\u003cspan style=\"color:#f92672\"\u003earticle\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ end }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ end }}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/details\u003e\n\n\u003cp\u003eThe beginning part of the shortcode converts the URL to the JSON endpoint of the toot. It then requests the resource and parses the JSON to display the toot like in the last section. With this, you can include the shortcode \u003ccode\u003edisplayOnlineToot\u003c/code\u003e to any Hugo markdown post.\u003c/p\u003e\n\u003ch2 id=\"archiving-toots\"\u003eArchiving Toots\u003c/h2\u003e\n\u003cp\u003eWith the setup above the Mastodon server will be queried and the toots created at build time. For a multitude of reasons, I prefer to archive the toot onto my own website and reference that during build time.\u003c/p\u003e\n\u003cp\u003eI wrote \u003ca href=\"https://github.com/Brandon-Rozek/website/blob/9947478c6c4e3f9f9f99c742c2c2287e51a98a29/archive_toot.py\"\u003ea python script\u003c/a\u003e that saves the JSON representation of the toot locally and returns \u003ccode\u003eSERVER-TOOTID\u003c/code\u003e which I can then use in another \u003ca href=\"https://github.com/Brandon-Rozek/pulp/blob/69eb3014056f0594a16a0417d8cd6b3b4cfc3750/layouts/shortcodes/displayToot.html\"\u003eshortcode that I called \u003ccode\u003edisplayToot\u003c/code\u003e\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;m happy to see that Mastodon doesn\u0026rsquo;t make it difficult to query and parse data from their platform. Within one toot there are a lot of possibilities for its type. For example, it can be a reply, a boost, or include a content warning label. To quickly get the basics (which includes replies), I ignored many of the fields. I would like to come back to this in the future and explore how to design for the other cases as well.\u003c/p\u003e\n",
    "date_published": "2022.05.20",
    "tags": ["Hugo","Mastodon"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "a10174e115a2"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/hugo-hiding-section-from-listing/",
    "url": "https://brandonrozek.com/blog/hugo-hiding-section-from-listing/",
    "title": "Hiding Section From Listing in Hugo",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn Hugo you can list all the sections using the following code\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ range .Site.Sections }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;\u003cspan style=\"color:#f92672\"\u003eli\u003c/span\u003e\u0026gt;\u0026lt;\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehref\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .Permalink }}\u0026#34;\u003c/span\u003e\u0026gt;{{ .Title }}\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003eli\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ end }}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHowever what if there\u0026rsquo;s a section that you want to hide for whatever reason? Let us say that you have a section labeled \u0026ldquo;unlisted\u0026rdquo; that you want to hide. The directory structure can look like this:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003econtent/\n  posts/\n  unlisted/\n  bookmarks/\n// ...\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThere are two types of hidden that I can think of:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eYou don\u0026rsquo;t want any pages within unlisted to render at all.\u003c/li\u003e\n\u003cli\u003eYou want it to render, but not appear in the section listing\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eFor the first case, \u003ca href=\"https://web.archive.org/web/20210125075337/https://filosophy.org/code/disabling-a-specific-section-in-hugo/\"\u003eFilosophy suggests\u003c/a\u003e to rename the section so that it starts with a dot. For example, \u003ccode\u003e.unlisted\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFor the second case, we need to introduce a page variable to help us choose when to display it. Let us call that page variable \u003ccode\u003ehidden\u003c/code\u003e. To set it to true, you need to add it to the frontmatter of \u003ccode\u003econtent/unlisted/_index.md\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e---\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003ehidden\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e---\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen replace the listing code with the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ range .Site.Sections }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  {{ if not .Params.hidden }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003eli\u003c/span\u003e\u0026gt;\u0026lt;\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehref\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;{{ .Permalink }}\u0026#34;\u003c/span\u003e\u0026gt;{{ .Title }}\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ea\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003eli\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  {{ end }}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{{ end }}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.05.19",
    "tags": ["Hugo"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "d71625f9235b"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/document-formats-and-plaintext/",
    "url": "https://brandonrozek.com/blog/document-formats-and-plaintext/",
    "title": "Document Formats and Plaintext",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eMicrosoft Word, Apple Pages, Google Docs, Libreoffice Writer all provide a method of writing and formatting text. This is then normally stored in a \u0026ldquo;binary\u0026rdquo; file. I put binary in quotes as they are often stored in a zip archive of XML files. However, because it\u0026rsquo;s in a zip archive, I cannot use standard plaintext tools to search within the document.\u003c/p\u003e\n\u003cp\u003eWhen would I need to search multiple files at once for a word? Well not only is this useful for programming to find usages of a function, I often use it to find out if I have written about a topic before.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egrep -R chocolate\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe command above returns the lines of files that contain the word \u0026ldquo;chocolate\u0026rdquo;. As of the time of writing, I had no such occurrence in my blog directory. Until now, mwahaha.\u003c/p\u003e\n\u003cp\u003eWhat\u0026rsquo;s the solution then? To write everything in \u003ccode\u003e.txt\u003c/code\u003e files? Kind of actually. We\u0026rsquo;ll write plaintext files but also include a bit of formatting. We can then \u003cem\u003epublish\u003c/em\u003e the document by converting it to a webpage (HTML) or a PDF document. The most popular formatting technique is \u003ca href=\"https://daringfireball.net/projects/markdown/\"\u003eMarkdown\u003c/a\u003e (often has the extension \u003ccode\u003e.md\u003c/code\u003e). That\u0026rsquo;s also the format that my blog posts are encoded in.\u003c/p\u003e\n\u003cp\u003eOne critique of Markdown is that the user does not have much control on the output presentation of the document. Tools like \u003ca href=\"https://pandoc.org/\"\u003ePandoc\u003c/a\u003e allow for custom stylesheets to control it a bit but it\u0026rsquo;s still limited. In my view, the most powerful plaintext document format is \u003ca href=\"https://www.latex-project.org/\"\u003eLaTex\u003c/a\u003e. Though it\u0026rsquo;s not the simplest to learn.\u003c/p\u003e\n\u003cp\u003eIf you switch to a plaintext editor like notepad, vscode, vim, etc. it might not come with built in spell check. Luckily on linux, there\u0026rsquo;s a helpful spell check program.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003easpell check filetocheck.md\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSo what plaintext formats do I currently use on a day-to-day?\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eBlog posts: Markdown\u003c/li\u003e\n\u003cli\u003eNotes: Markdown or random scribbles on physical pieces of paper\u003c/li\u003e\n\u003cli\u003eAcademic papers: LaTex\u003c/li\u003e\n\u003cli\u003ePresentations: LaTex (unless they need video)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIf you\u0026rsquo;re interested in learning how to work with plaintext and other cool things, check out the \u003ca href=\"https://plaintextproject.online/\"\u003ePlain Text Project\u003c/a\u003e by \u003ca href=\"https://scottnesbitt.net/\"\u003eScott Nesbitt\u003c/a\u003e.\u003c/p\u003e\n",
    "date_published": "2022.05.19",
    "tags": ["Documentation"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "b339b9d9793b"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/writing-simple-scripts/",
    "url": "https://brandonrozek.com/blog/writing-simple-scripts/",
    "title": "On Writing Simple Scripts",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI generally prefer a simpler solution to a problem if possible. This comes especially true with scripting. If I write a small script for something like say my website I generally have three requirements of the system:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eNo dependencies outside the language\u003c/li\u003e\n\u003cli\u003eLanguage is interpreted (I don\u0026rsquo;t want to worry about build dependencies)\u003c/li\u003e\n\u003cli\u003eComes preinstalled on systems I care about\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eI primarily work on Linux systems and very rarely don\u0026rsquo;t. Therefore, the simplest scripting language for me to write in is Bash. In fact, I generally reach for that first. If what I need to do is sufficiently complicated, then the next language I\u0026rsquo;ll reach for is Python.\u003c/p\u003e\n\u003cp\u003eThough that begs the question, what is sufficiently complicated? Here are some tasks that I don\u0026rsquo;t think Bash is suited for:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eData Parsing\n\u003cul\u003e\n\u003cli\u003eI know \u003ccode\u003eack\u003c/code\u003e, \u003ccode\u003esed\u003c/code\u003e, and \u003ccode\u003egrep\u003c/code\u003e exist, but they\u0026rsquo;re complicated and unintuitive to use. We write programs for \u003cem\u003epeople first\u003c/em\u003e, computers second.\u003c/li\u003e\n\u003cli\u003eHarder to parse JSON. The program \u003ccode\u003ejq\u003c/code\u003e exists but that\u0026rsquo;s not part of the GNU coreutils.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eMath\n\u003cul\u003e\n\u003cli\u003eI believe \u003ccode\u003ebc\u003c/code\u003e is the easiest command to use to process arithmetical expressions. Though this also then involves constructing strings to pass into bc. For example: \u003ccode\u003eresult=$(echo \u0026quot;1 + 5\u0026quot; | bc)\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eAnything requiring abstract data types\n\u003cul\u003e\n\u003cli\u003e(Associative) arrays in bash are scary to me\u003c/li\u003e\n\u003cli\u003eSlawomir\u0026rsquo;s functional programming in \u003ca href=\"https://github.com/ssledz/bash-fun\"\u003ebash library\u003c/a\u003e makes it more bearable. (\u003ca href=\"https://github.com/Brandon-Rozek/bash-fun\"\u003eMy fork\u003c/a\u003e)\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eArgument Parsing\n\u003cul\u003e\n\u003cli\u003eAttempted to \u003ca href=\"/blog/bashpartialargparse/\"\u003eparse arguments in bash\u003c/a\u003e, but \u003ca href=\"/blog/python-argpase/\"\u003eparsing arguments in Python\u003c/a\u003e is much cleaner to me.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eSo why Python?\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eComes preinstalled on most Linux systems as they\u0026rsquo;re often used in desktop environments\u003c/li\u003e\n\u003cli\u003eCurrently has developer mindshare so others are apt to understand the scripts\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eBoth those reasons are likely to make it so that my choice will change over time. Perl used to be the very popular choice for scripting\u0026hellip;\u003c/p\u003e\n",
    "date_published": "2022.05.19",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "63c82315e4f5"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/python-argparse/",
    "url": "https://brandonrozek.com/blog/python-argparse/",
    "title": "Python Argument Parser",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cem\u003eFor a much better description of argument parsing in Python, please visit \u003ca href=\"https://docs.python.org/3/library/argparse.html\"\u003ehttps://docs.python.org/3/library/argparse.html\u003c/a\u003e.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;m creating this post because even though it\u0026rsquo;s not too complicated, I keep forgetting how to do argument parsing in Python. I also tricked myself into thinking each time that I already wrote a blog post on this. Let\u0026rsquo;s correct this once and for all and include a quick example!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e argparse\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eparser \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e argparse\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eArgumentParser(description\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Description to show in help\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eparser\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd_argument(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;pos_arg1\u0026#34;\u003c/span\u003e, type\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003estr, help\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Required positional argument\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eparser\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd_argument(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;--flag1\u0026#34;\u003c/span\u003e, type\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003estr, help\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Optional flag argument\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eparser\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd_argument(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;--flag2\u0026#34;\u003c/span\u003e, type\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eint, required\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eTrue\u003c/span\u003e, help\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Required flag argument\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eparser\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd_argument(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;--flag3\u0026#34;\u003c/span\u003e, type\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eint, default\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, help\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Optional flag argument \u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e    with default value of 0\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eargs \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e vars(parser\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eparse_args())\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow if you call the program with no arguments you\u0026rsquo;ll get the following message:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eusage: testarg.py [-h] [--flag1 FLAG1] --flag2 FLAG2 pos_arg1\ntestarg.py: error: the following arguments are required: pos_arg1, --flag2\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis comes with a built in \u003ccode\u003e-h\u003c/code\u003e as well!\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eusage: testarg.py [-h] [--flag1 FLAG1] --flag2 FLAG2 pos_arg1\n\nDescription to show in help\n\npositional arguments:\n  pos_arg1       Required positional argument\n\noptional arguments:\n  -h, --help            show this help message and exit\n  --flag1 FLAG1         Optional flag argument\n  --flag2 FLAG2         Required flag argument\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWithin the code you can parse the argument names by accessing the \u003ccode\u003eargs\u003c/code\u003e dictionary.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epos_arg1 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e args[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;pos_arg1\u0026#39;\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eflag1 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e args[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;flag1\u0026#39;\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eflag2 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e args[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;flag2\u0026#39;\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.05.16",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "7d4c148bd2d0"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/common-mistake-induction-proofs/",
    "url": "https://brandonrozek.com/blog/common-mistake-induction-proofs/",
    "title": "Most Common Mistake in Induction Proofs",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOne of the most common mistakes I see in induction proofs is assuming the recursive case and working backwards towards the induction hypothesis. This may be fine for formulas that are symmetric like those involving equality, but this way of proving induction fails if not. This post will show such example.\u003c/p\u003e\n\u003ch2 id=\"question\"\u003eQuestion\u003c/h2\u003e\n\u003cp\u003eProve $x - 1 \\ge \\frac{x}{2}$ for $x \\in \\mathbb{Z}^+$\u003c/p\u003e\n\u003ch2 id=\"base-case\"\u003eBase Case\u003c/h2\u003e\n\u003cp\u003eLet $x = 1$. Then,\n$$\n\\begin{align*}\n1 -1 \u0026amp;\\ge \\frac{1}{2} \\\\\n0 \u0026amp;\\ge 0 \\checkmark\n\\end{align*}\n$$\nThe right side simplifies to zero since we\u0026rsquo;re doing integer division.\u003c/p\u003e\n\u003ch2 id=\"recursive-case-correct\"\u003eRecursive Case (Correct)\u003c/h2\u003e\n\u003cp\u003eLet us assume the induction hypothesis, that is, $x_n - 1 \\ge \\frac{x_n}{2}$. We will show that $x_{n + 1} - 1 \\ge \\frac{x_{n + 1}}{2}$.\u003c/p\u003e\n\u003cp\u003eWe know $x_{n + 1} = x_n + 1$, therefore, $x_n = x_{n + 1} - 1$.\u003c/p\u003e\n\u003cp\u003eSubstituting into our inductive hypothesis:\n$$\n\\begin{align*}\n(x_{n + 1} - 1) - 1 \u0026amp;\\ge \\frac{x_{n + 1} - 1}{2} \\iff \\\\\n(x_{n + 1} - 1) - 1 \u0026amp;\\ge \\frac{x_{n + 1}}{2} - \\frac{1}{2} \\iff \\\\\nx_{n + 1} - 1 \u0026amp;\\ge \\frac{x_{n + 1}}{2} + \\frac{1}{2} \\implies \\\\\nx_{n + 1} - 1 \u0026amp;\\ge \\frac{x_{n + 1}}{2}\n\\end{align*}\n$$\nTherefore, via induction this theorem holds. Note that the last step was only an implication, this is where the symmetry breaks.\u003c/p\u003e\n\u003ch2 id=\"recursive-case-incorrect\"\u003eRecursive Case (Incorrect)\u003c/h2\u003e\n\u003cp\u003eNow let us see how we can go astray if we start from the wrong direction. Substituting what we know about $x_n$ and $x_{n + 1}$:\n$$\n\\begin{align*}\nx_{n + 1} - 1 \u0026amp;\\ge \\frac{x_{n + 1}}{2} \\iff \\\\\n(x_n + 1) - 1 \u0026amp;\\ge \\frac{(x_n + 1)}{2} \\iff \\\\\nx_n \u0026amp;\\ge \\frac{x_n}{2} + \\frac{1}{2} \\iff \\\\\nx_n -1 \u0026amp;\\ge \\frac{x_n}{2} - \\frac{1}{2}\n\\end{align*}\n$$\nSadly from here we cannot imply that $x_n - 1 \\ge \\frac{x_n}{2}$.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eRemember, when performing the induction step of the proof:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eState the induction hypotheses\u003c/li\u003e\n\u003cli\u003eState what formulas you know about the various variables\u003c/li\u003e\n\u003cli\u003eSubstitute into the \u003cstrong\u003einduction hypothesis\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003eSimplify until you reach the $n+1$th step.\u003c/li\u003e\n\u003c/ul\u003e\n",
    "date_published": "2022.05.15",
    "tags": ["Math"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "9a14aa49d67a"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/auto-updating-podman-containers/",
    "url": "https://brandonrozek.com/blog/auto-updating-podman-containers/",
    "title": "Automatically Updating Podman Containers",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eRecently, I have been  \u003ca href=\"/blog/rootless-docker-compose-podman/\"\u003etransitioning to Podman\u003c/a\u003e  for running my container infrastructure. In the process, I brought over Watchtower which I have previously used for auto-updating docker containers. Before doing so, I didn\u0026rsquo;t check its \u003ca href=\"https://github.com/containrrr/watchtower/issues/1060\"\u003ecompatibility\u003c/a\u003e (whoops) and found a few of my containers would every other week or so not come back up.\u003c/p\u003e\n\u003cp\u003eI then remembered that I restart my server for general system updates almost every day. What if I perform the podman container updates on start up? After modiyfing my systemd service to include an extra field called \u003ccode\u003eExecStartPre\u003c/code\u003e and removing Watchtower, I found no more missing containers! The field \u003ccode\u003eExecStartPre\u003c/code\u003e performs a pull (update) before starting up the containers via \u003ccode\u003eExecStart\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eDocker Compose Application Service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRequires\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enetwork.target podman.socket\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAfter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enetwork.target podman.socket\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eoneshot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRemainAfterExit\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eyes\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWorkingDirectory\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/home/brandonrozek\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStartPre\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/docker-compose pull\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/docker-compose up -d --force-recreate\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStop\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/docker-compose down\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eTimeoutStartSec\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.05.15",
    "tags": ["Linux","Containers"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "9fe3d313dea8"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/recently-2022-05-15/",
    "url": "https://brandonrozek.com/blog/recently-2022-05-15/",
    "title": "Recently (May 2022)",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cstrong\u003eProfessional:\u003c/strong\u003e I started my PhD program back in August 2021.\nSo far, I\u0026rsquo;ve met many wonderful people including\nbut not limited to the folks at the RAIR lab which\nI\u0026rsquo;m now a part of.\u003c/p\u003e\n\u003cp\u003eFor my first two semesters I was a teaching assistant.\nThe first was Introduction to Computer Science and the\nsecond was Principles of Software.\nBoth of these classes had what I would consider a large amount\nof students (\u0026gt; 300), which meant that there were multiple TAs\nsplitting the class into smaller lab/recitation sections.\nHonestly those labs and recitations were the best part of the TAing experience.\u003c/p\u003e\n\u003cp\u003eIn Summer 2022, I will be at IBM Research in Yorktown Heights, NY.\nI\u0026rsquo;m looking forward to having more research dedicated time.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePersonal Life:\u003c/strong\u003e Living in Upstate NY, this is the coldest winter I\u0026rsquo;ve been through.\nThere are many of you in far colder places, but I was not mentally prepared for the\nconstant 15 degrees Fahrenheit weather.\nWhat helped keep me warm:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eWindow Insulator Kit (lowers the amount of heat escape)\u003c/li\u003e\n\u003cli\u003eThermal underlayers (top and bottom)\u003c/li\u003e\n\u003cli\u003eSoup (Nice and warm)\u003c/li\u003e\n\u003cli\u003eHeated Blanket (much cheaper than a space heater)\u003c/li\u003e\n\u003cli\u003eSnow spikes (for hiking in the winter)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eNow that we\u0026rsquo;re approaching summer again,\nI look forward to going on more hikes.\nYou can check out some of the trails I completed on my\n\u003ca href=\"https://www.alltrails.com/members/brandon-rozek/completed\"\u003eAllTrails profile\u003c/a\u003e.\u003c/p\u003e\n",
    "date_published": "2022.05.15",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/finding-cool-people-on-mastodon/",
    "url": "https://brandonrozek.com/blog/finding-cool-people-on-mastodon/",
    "title": "Finding Cool People on Mastodon",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eMastodon is a cool federated micro-blogging platform that can serve as an alternative to Twitter. One of its primary features is that it shows what they call toots from only people you follow in reverse-chronological order. Nowadays, large tech companies employ recommendation engines to surface content from users (you don\u0026rsquo;t even need to follow) in a non-chronological order driven by the number of interactions users similar to you had with the content. The lack of recommendation engine in Mastodon can be a blessing as low-quality emotional content tends to drive more interactions. However, this can also be a curse as cool new people don\u0026rsquo;t naturally surface in your feed. The following are the techniques I employ to consistently seek out cool new people:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"#local-feed\"\u003eLocal Feed\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#expert-mode\"\u003eExpert Mode\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#introduction-and-introductions\"\u003e#Introduction and #Introductions\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#fedifollows\"\u003eFediFollows\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#trunks\"\u003eTrunks\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#fediverseinfo\"\u003eFediverse.Info\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"local-feed\"\u003eLocal Feed\u003c/h2\u003e\n\u003cp\u003eThe easiest and lowest effort way to find new people is to check out what\u0026rsquo;s getting tooted in your local homeserver. These homeservers are often communities within themselves and finding a good fit will help with your initial experience with Mastodon.\u003c/p\u003e\n\u003cp\u003eTo get a feel for how the community within a homeserver is like, you can check their public feed by appending \u003ccode\u003e/public\u003c/code\u003e at the end of the server URL. For example: \u003ca href=\"https://fosstodon.org/public\"\u003ehttps://fosstodon.org/public\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eHere are some homeservers which I think have great communities and are friendly. This is by no means a complete list:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://fosstodon.org\"\u003eFosstodon.org\u003c/a\u003e (Free and Open Source Software Crowd)\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://mastodon.art\"\u003eMastodon.art\u003c/a\u003e (Art community)\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://mastodon.radio\"\u003eMastodon.radio\u003c/a\u003e (Amateur Radio Community)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eFor a more complete list checkout \u003ca href=\"https://instances.social\"\u003ehttps://instances.social\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eIf you\u0026rsquo;ve already created an account, but want to move to a different homeserver. There\u0026rsquo;s a \u003ca href=\"https://docs.joinmastodon.org/user/moving/\"\u003eguide to moving accounts available on the wiki\u003c/a\u003e. This process luckily preserves your followers and follows.\u003c/p\u003e\n\u003ch2 id=\"expert-mode\"\u003eExpert Mode\u003c/h2\u003e\n\u003cp\u003eWhen you first create an account on Mastodon you will have an interface similar to this following.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/202205151932.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eOne thing you\u0026rsquo;ll notice immediately is that it\u0026rsquo;s very limited. You can only view one feed at a time, and you can\u0026rsquo;t set an option to see your feed and multiple tags in one place. This is where the advanced interface comes in. Go to Preferences -\u0026gt; Appearence -\u0026gt; Check \u0026ldquo;Enable Advanced Web Interface\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eThis will then enable something similar to below:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/202205151934.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eNote the ordering of the feeds I have pinned:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eHome feed\u003c/li\u003e\n\u003cli\u003eTags I am interested in\u003c/li\u003e\n\u003cli\u003eLocal Timeline\u003c/li\u003e\n\u003cli\u003e#Introduction\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eTags are one of the best built in methods of discovery that Mastodon offers. Feel free to liberally use tags in your future toots.\u003c/p\u003e\n\u003ch2 id=\"introduction-and-introductions\"\u003e#Introduction and #Introductions\u003c/h2\u003e\n\u003cp\u003eA tradition on Mastodon is that whenever someone joins they create what we call a #Introduction post. This is helpful to people new on the platform as others can decide whether to follow back based on a more complete profile, and it helps people like me find like-minded folks. For example, here\u0026rsquo;s mine:\u003c/p\u003e\n\n\n\n\n  \n    \n  \n\nw\n\n\n\n\n\n\u003carticle class=\"toot h-entry\"\u003e\n    \u003cdiv rel=\"author\" class=\"h-card p-author toot-avatar\"\u003e\n      \u003cimg class=\"u-photo\" width=50 src=\"https://cdn.fosstodon.org/accounts/avatars/108/219/415/927/856/966/original/c007afd0c6749859.png\"/\u003e\n      \u003cspan style=\"display: none;\"\u003eBrandon Rozek\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cp class=\"date\"\u003eTooted on \u003ctime class=\"dt-published\" datetime='2022-04-30T18:27:03.588Z'\u003eApril 30, 2022 18:27\u003c/time\u003e\u003c/p\u003e\n    \u003cdiv class=\"e-content p-name\"\u003e\n      \u003cp\u003e\u003ca href=\"https://fosstodon.org/tags/Introduction\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003eIntroduction\u003c/span\u003e\u003c/a\u003e Hi everyone! I\u0026#39;m a PhD student in computer science, studying the intersection between formal logic and artificial intelligence.\u003c/p\u003e\u003cp\u003eI enjoy and love all things \u003ca href=\"https://fosstodon.org/tags/foss\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003efoss\u003c/span\u003e\u003c/a\u003e , I am a \u003ca href=\"https://fosstodon.org/tags/fedora\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003efedora\u003c/span\u003e\u003c/a\u003e user and often tinker around with my \u003ca href=\"https://fosstodon.org/tags/homelab\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003ehomelab\u003c/span\u003e\u003c/a\u003e.\u003c/p\u003e\u003cp\u003eI also fumble around with \u003ca href=\"https://fosstodon.org/tags/hamradio\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003ehamradio\u003c/span\u003e\u003c/a\u003e and enjoy vibing outdoors when the weather is nice. When I\u0026#39;m extra ambitious, I\u0026#39;ll add a hike in!\u003c/p\u003e\n      \n    \u003c/div\u003e\n    \u003ca class=\"u-url\" style=\"display: none\"\u003e\u003c/a\u003e\n    \n    \u003ca class=\"p-category\" href=\"https://fosstodon.org/tags/hamradio\" style=\"display: none;\"\u003ehamradio\u003c/a\u003e\n    \n    \u003ca class=\"p-category\" href=\"https://fosstodon.org/tags/homelab\" style=\"display: none;\"\u003ehomelab\u003c/a\u003e\n    \n    \u003ca class=\"p-category\" href=\"https://fosstodon.org/tags/fedora\" style=\"display: none;\"\u003efedora\u003c/a\u003e\n    \n    \u003ca class=\"p-category\" href=\"https://fosstodon.org/tags/foss\" style=\"display: none;\"\u003efoss\u003c/a\u003e\n    \n    \u003ca class=\"p-category\" href=\"https://fosstodon.org/tags/introduction\" style=\"display: none;\"\u003eintroduction\u003c/a\u003e\n    \n    \u003cdiv class=\"toot-social\"\u003e\n        \n        \n        \n        \n        \n        \n        \u003cspan\u003e\n          \u003cspan class='fas fa-share'\u003e\u003c/a\u003e\n          6\n        \u003c/span\u003e\n        \u003cspan\u003e\n          \u003cspan class='fas fa-retweet'\u003e\u003c/a\u003e\n          9\n        \u003c/span\u003e\n        \u003cspan\u003e\n          \u003cspan class='fas fa-star'\u003e\u003c/a\u003e\n          41\n        \u003c/span\u003e\n        \u003cspan\u003e| Source: \u003ca class=\"u-syndication\" href=\"https://fosstodon.org/@brozek/108222429500713598\"\u003ehttps://fosstodon.org/@brozek/108222429500713598\u003c/a\u003e\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/article\u003e\n  \n  \n  \n\n\u003cp\u003eHere\u0026rsquo;s what empty profiles look like to us:\u003c/p\u003e\n\n\n\n\n  \n    \n  \n\nw\n\n\n\n\n\n\u003carticle class=\"toot h-entry\"\u003e\n    \u003cdiv rel=\"author\" class=\"h-card p-author toot-avatar\"\u003e\n      \u003cimg class=\"u-photo\" width=50 src=\"https://cdn.fosstodon.org/accounts/avatars/108/198/204/663/922/209/original/9f0132b39ab1c154.png\"/\u003e\n      \u003cspan style=\"display: none;\"\u003eR. L. Dane\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cp class=\"date\"\u003eTooted on \u003ctime class=\"dt-published\" datetime='2022-05-09T03:13:06.724Z'\u003eMay 9, 2022 03:13\u003c/time\u003e\u003c/p\u003e\n    \u003cdiv class=\"e-content p-name\"\u003e\n      \u003cp\u003eDear new users:\u003cbr /\u003eYou probably won\u0026#39;t get followed back if you don\u0026#39;t have any toots/posts or a bio. \u003c/p\u003e\u003cp\u003eIt\u0026#39;s like introducing yourself while wearing a full mask and costume and using a voice changer.\u003c/p\u003e\n      \n    \u003c/div\u003e\n    \u003ca class=\"u-url\" style=\"display: none\"\u003e\u003c/a\u003e\n    \n    \u003cdiv class=\"toot-social\"\u003e\n        \n        \n        \n        \n        \n        \n        \u003cspan\u003e\n          \u003cspan class='fas fa-share'\u003e\u003c/a\u003e\n          2\n        \u003c/span\u003e\n        \u003cspan\u003e\n          \u003cspan class='fas fa-retweet'\u003e\u003c/a\u003e\n          3\n        \u003c/span\u003e\n        \u003cspan\u003e\n          \u003cspan class='fas fa-star'\u003e\u003c/a\u003e\n          15\n        \u003c/span\u003e\n        \u003cspan\u003e| Source: \u003ca class=\"u-syndication\" href=\"https://fosstodon.org/@RL_Dane/108269796505838110\"\u003ehttps://fosstodon.org/@RL_Dane/108269796505838110\u003c/a\u003e\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/article\u003e\n  \n  \n  \n\n\u003ch2 id=\"fedifollows\"\u003eFediFollows\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"mailto:FediFollows@mastodon.online\"\u003eFediFollows@mastodon.online\u003c/a\u003e is an account that regularly recommends new accounts to follow. In fact, they even have these accounts \u003ca href=\"https://mastodon.online/@FediFollows/108276705471084305\"\u003eorganized by topic\u003c/a\u003e. Here\u0026rsquo;s an example of a recent toot as of the time of writing:\u003c/p\u003e\n\n\n\n\n  \n    \n  \n\nw\n\n\n\n\n\n\u003carticle class=\"toot h-entry\"\u003e\n    \u003cdiv rel=\"author\" class=\"h-card p-author toot-avatar\"\u003e\n      \u003cimg class=\"u-photo\" width=50 src=\"https://files.mastodon.online/accounts/avatars/000/023/155/original/415df37c0730912a.png\"/\u003e\n      \u003cspan style=\"display: none;\"\u003eFediFollows\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cp class=\"date\"\u003eTooted on \u003ctime class=\"dt-published\" datetime='2022-05-15T20:49:45.214Z'\u003eMay 15, 2022 20:49\u003c/time\u003e\u003c/p\u003e\n    \u003cdiv class=\"e-content p-name\"\u003e\n      \u003cp\u003eToday\u0026#39;s recommended follows:\u003c/p\u003e\u003cp\u003e🌟 \u003cspan class=\"h-card\"\u003e\u003ca href=\"https://mastodon.art/@wandering_jas\" class=\"u-url mention\"\u003e@\u003cspan\u003ewandering_jas\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e - Draws intricate black and white drawings often inspired by games such as Zelda\u003c/p\u003e\u003cp\u003e🌟 \u003cspan class=\"h-card\"\u003e\u003ca href=\"https://mastodon.online/@astro_jcm\" class=\"u-url mention\"\u003e@\u003cspan\u003eastro_jcm\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e - Astronomer and media officer at the European Southern Observatory in the Chilean desert\u003c/p\u003e\u003cp\u003e🌟 \u003cspan class=\"h-card\"\u003e\u003ca href=\"https://mastodon.radio/@M0YNG\" class=\"u-url mention\"\u003e@\u003cspan\u003eM0YNG\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e - Amateur (\u0026quot;ham\u0026quot;) radio enthusiast who runs the mastodon.radio server\u003c/p\u003e\u003cp\u003e🌟 \u003cspan class=\"h-card\"\u003e\u003ca href=\"https://libranet.de/profile/fediphoto_lineage\" class=\"u-url mention\"\u003e@\u003cspan\u003efediphoto_lineage\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e - App for Android phones which makes it as easy as possible to share photos to the Fediverse\u003c/p\u003e\u003cp\u003e🌟 \u003cspan class=\"h-card\"\u003e\u003ca href=\"https://mastodon.art/@monarobot\" class=\"u-url mention\"\u003e@\u003cspan\u003emonarobot\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e - Creates art inspired by the Maya civilisation\u003c/p\u003e\u003cp\u003e(1/2)\u003c/p\u003e\n      \n    \u003c/div\u003e\n    \u003ca class=\"u-url\" style=\"display: none\"\u003e\u003c/a\u003e\n    \n    \u003cdiv class=\"toot-social\"\u003e\n        \n        \n        \n        \n        \n        \n        \u003cspan\u003e\n          \u003cspan class='fas fa-share'\u003e\u003c/a\u003e\n          1\n        \u003c/span\u003e\n        \u003cspan\u003e\n          \u003cspan class='fas fa-retweet'\u003e\u003c/a\u003e\n          2\n        \u003c/span\u003e\n        \u003cspan\u003e\n          \u003cspan class='fas fa-star'\u003e\u003c/a\u003e\n          7\n        \u003c/span\u003e\n        \u003cspan\u003e| Source: \u003ca class=\"u-syndication\" href=\"https://mastodon.online/@FediFollows/108307925251519968\"\u003ehttps://mastodon.online/@FediFollows/108307925251519968\u003c/a\u003e\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/article\u003e\n  \n  \n  \n\n\u003cp\u003eI recommend you follow FediFollows and checkout any profiles that interest you!\u003c/p\u003e\n\u003ch2 id=\"trunks\"\u003eTrunks\u003c/h2\u003e\n\u003cp\u003eWhile FediFollows provides recommendations of accounts to follow (both organizations and fellow humans), trunks provide a list of people to follow by topic.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://communitywiki.org/trunk\"\u003ehttps://communitywiki.org/trunk\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eThe list is maintained by a few kind volunteers stated on the webpage. People can sign up to be on multiple trunks by messaging the admins.\u003c/p\u003e\n\u003ch2 id=\"fediverseinfo\"\u003eFediverse.info\u003c/h2\u003e\n\u003cp\u003eThe last resource that I\u0026rsquo;ll share is \u003ca href=\"https://fediverse.info\"\u003ehttps://fediverse.info\u003c/a\u003e. This is like the trunks above where it\u0026rsquo;s a site that maintains lists of accounts organized by topic. However, instead of being manually vetted by a few admins, anyone can add their profile to this website. The full instructions are on the website but it\u0026rsquo;s based on tags in one\u0026rsquo;s bio.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eThe lack of recommendation engine has driven the Mastodon community to create new and novel ways of discovering new people. It wouldn\u0026rsquo;t surprise me if within the year a whole new set of tools are made available to us. Though I have to say, the default of not showing unfollowed accounts in our feeds is really nice. It means that our Mastodon experience is truly unique to us, and that we can curate them to our liking.\u003c/p\u003e\n",
    "date_published": "2022.05.15",
    "tags": ["Mastodon"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "50c97fc8aa3c"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/why-i-pesos-from-mastodon/",
    "url": "https://brandonrozek.com/blog/why-i-pesos-from-mastodon/",
    "title": "Why I PESOS From Mastodon",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe \u003ca href=\"https://indieweb.org\"\u003eIndieWeb movement\u003c/a\u003e is focused on people controlling their own experience on the web. They accomplish this by encouraging everyone to have their own personal website. Followers can then either subscribe to the \u003ca href=\"https://aboutfeeds.com/\"\u003eRSS feed\u003c/a\u003e and provide \u003ca href=\"https://indieweb.org/Webmention\"\u003eWebmentions\u003c/a\u003e back to the post, or they can interact with the material directly on another website like \u003ca href=\"https://joinmastodon.org/\"\u003eMastodon\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThe recommended syndicate or copy content is through a technique called \u003ca href=\"https://indieweb.org/POSSE\"\u003ePublish Own Site Syndicate Elsewhere (POSSE)\u003c/a\u003e. First the author posts on their own website, and then a function takes that post and publishes it on another website.\u003c/p\u003e\n\u003cp\u003eHowever, this is not what I am currently doing to \u003ca href=\"/toots/\"\u003eshow my Mastodon toots\u003c/a\u003e. Instead I have implemented \u003ca href=\"https://indieweb.org/PESOS\"\u003ePublish Elsewhere Syndicate Own Site (PESOS)\u003c/a\u003e. In this model, I first publish directly on Mastodon and then have a function query the Mastodon server to update my personal website.\u003c/p\u003e\n\u003ch2 id=\"advantages-to-pesos\"\u003eAdvantages to PESOS\u003c/h2\u003e\n\u003cp\u003eThe main advantage of PESOS is that my primary interaction with Mastodon is through their web and Android app Tusky. Interacting the same way as others on the platform gives me a stronger sense of community when using Mastodon. I don\u0026rsquo;t feel as if I am only just copying posts from my site onto Mastodon. There are also many features built in when posting on Mastodon that I would have to design for and replicate otherwise:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eContent warnings\u003c/li\u003e\n\u003cli\u003eReplies\u003c/li\u003e\n\u003cli\u003eBoosts\u003c/li\u003e\n\u003cli\u003eCharacter Limits\u003c/li\u003e\n\u003cli\u003eAlt Description in Images\u003c/li\u003e\n\u003cli\u003ePost Permissions\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"disadvantages-to-pesos\"\u003eDisadvantages to PESOS\u003c/h2\u003e\n\u003cp\u003eThe primary way that people are following me is within Mastodon itself as opposed to the RSS feed on my website. This means that if the Mastodon server ever abruptly goes down, then I will lose contact with all of my followers. Mastodon, however, does have some protection against this as a federated network.  Mainly the ability to \u003ca href=\"https://docs.joinmastodon.org/user/moving/\"\u003emove your profile\u003c/a\u003e from one Mastodon instance to another.  This gives me a more secure feeling that Mastodon itself won\u0026rsquo;t shut down in the near future.\u003c/p\u003e\n",
    "date_published": "2022.05.15",
    "tags": ["Mastodon"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "56667e2c305"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/git-bisect-broken-builds/",
    "url": "https://brandonrozek.com/blog/git-bisect-broken-builds/",
    "title": "Which commit broke the build? Using Git Bisect",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLets imagine a scenario where in the latest merge a test\nstarts failing. Lets say these tests are saved\nin \u003ccode\u003etest.sh\u003c/code\u003e. Instead of having to test each individual\ncommit in the merge, to see where the test fails, luckily\n\u003ccode\u003egit bisect\u003c/code\u003e narrows it down in a more efficient way!\u003c/p\u003e\n\u003cp\u003eTo use:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit bisect start \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003egood\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003ebad\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit bisect run test.sh\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhere \u003ccode\u003e[good]\u003c/code\u003e and \u003ccode\u003e[bad]\u003c/code\u003e are replaced with their respective\ncommit hashes.\u003c/p\u003e\n\u003cp\u003eUnder the hood, Git will run a binary search between the good\nand bad nodes in the commit tree.\u003c/p\u003e\n\u003cp\u003eAs a reminder, don\u0026rsquo;t forget to make \u003ccode\u003etest.sh\u003c/code\u003e an executable.\nStarting in Git 2.36 it will provide a warning, but earlier\nversions will perform the search anyways even with it all\nfailing.\u003c/p\u003e\n\u003cp\u003eRead more: \u003ca href=\"https://git-scm.com/docs/git-bisect\"\u003ehttps://git-scm.com/docs/git-bisect\u003c/a\u003e\u003c/p\u003e\n",
    "date_published": "2022.05.03",
    "tags": ["Git"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "1ef72415ab75"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/openmpi-fedora/",
    "url": "https://brandonrozek.com/blog/openmpi-fedora/",
    "title": "OpenMPI on Fedora",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eTo use use the OpenMPI compilers (mpicc, mpic++, etc.) and mpirun\non Fedora, you\u0026rsquo;ll need to install the openmpi package as well\nas set up the envionrment paths correctly.\u003c/p\u003e\n\u003cp\u003eTo install:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dnf install openmpi openmpi-devel\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen to set up the environmental variables correctly\nso that PATH is set. You\u0026rsquo;ll need to use the\n\u003ca href=\"https://modules.readthedocs.io/en/latest/\"\u003eenvironment modules\u003c/a\u003e\nprogram installed by default on Fedora. First, you\u0026rsquo;ll\nneed to source it:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esource /etc/profile.d/modules.sh\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow you can load in the OpenMPI module\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emodule load mpi/openmpi-x86_64\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFinally, with these changes you can use the compiler\ntools and runner. Do note that you\u0026rsquo;ll have to source\nand load the OpenMPI module for every shell you open\nunless you add it within \u003ccode\u003e$HOME/.bashrc\u003c/code\u003e.\u003c/p\u003e\n",
    "date_published": "2022.05.03",
    "tags": ["Fedora"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "1c0c98b99e8a"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/rclone/",
    "url": "https://brandonrozek.com/blog/rclone/",
    "title": "Rclone: The Swiss Army Knife of Cloud Storage",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://rclone.org/\"\u003eRclone\u003c/a\u003e is a terminal application that manages remote storage. It supports the standard remote filesystem protocols such as S3, (S)FTP, WebDAV as well as easy to use integration with cloud providers such as Amazon S2, Backblaze B2, Google Drive, and others. We can even perform regular backups using \u003ca href=\"https://restic.net/\"\u003eRestic\u003c/a\u003e with Rclone underneath. With all these features, it\u0026rsquo;s easy to configure all remote storage within Rclone and then synchronize the configuration file across machines.\u003c/p\u003e\n\u003cp\u003eOutline:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"#setting-up-a-remote\"\u003eSetting up a remote\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#mounting-a-remote\"\u003eMounting a remote\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#serving-a-remote\"\u003eServing a remote\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#conclusion\"\u003eConclusion\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"setting-up-a-remote\"\u003eSetting up a remote\u003c/h2\u003e\n\u003cp\u003eBelow is an example of setting up a WebDAV remote.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erclone config\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eNo remotes found - make a new one\nn) New remote\ns) Set configuration password\nq) Quit config\nn/s/q\u0026gt; n\nname\u0026gt; WebDav-Test\nType of storage to configure.\nEnter a string value. Press Enter for the default (\u0026#34;\u0026#34;).\nChoose a number from below, or type in your own value\n 1 / 1Fichier\n   \\ \u0026#34;fichier\u0026#34;\n\t...\n31 / Webdav\n   \\ \u0026#34;webdav\u0026#34;\n\t...\n35 / seafile\n   \\ \u0026#34;seafile\u0026#34;\nStorage\u0026gt; 31\n** See help for webdav backend at: https://rclone.org/webdav/ **\n\nURL of http host to connect to\nEnter a string value. Press Enter for the default (\u0026#34;\u0026#34;).\nChoose a number from below, or type in your own value\n 1 / Connect to example.com\n   \\ \u0026#34;https://example.com\u0026#34;\nurl\u0026gt; https://webdav.example.com\nName of the Webdav site/service/software you are using\nEnter a string value. Press Enter for the default (\u0026#34;\u0026#34;).\nChoose a number from below, or type in your own value\n 1 / Nextcloud\n   \\ \u0026#34;nextcloud\u0026#34;\n\t...\n 4 / Other site/service or software\n   \\ \u0026#34;other\u0026#34;\nvendor\u0026gt; 4\nUser name\nEnter a string value. Press Enter for the default (\u0026#34;\u0026#34;).\nuser\u0026gt; brandon\nPassword.\ny) Yes type in my own password\ng) Generate random password\nn) No leave this optional password blank (default)\ny/g/n\u0026gt; y\nEnter the password:\npassword:\nConfirm the password:\npassword:\nBearer token instead of user/pass (eg a Macaroon)\nEnter a string value. Press Enter for the default (\u0026#34;\u0026#34;).\nbearer_token\u0026gt;\nEdit advanced config? (y/n)\ny) Yes\nn) No (default)\ny/n\u0026gt; n\nRemote config\n--------------------\n[WebDav-Test]\nurl = https://webdav.example.com\nvendor = other\nuser = brandon\npass = *** ENCRYPTED ***\n--------------------\ny) Yes this is OK (default)\ne) Edit this remote\nd) Delete this remote\ny/e/d\u0026gt; y\nCurrent remotes:\n\nName                 Type\n====                 ====\nWebDav-Test          webdav\n\ne) Edit existing remote\nn) New remote\nd) Delete remote\nr) Rename remote\nc) Copy remote\ns) Set configuration password\nq) Quit config\ne/n/d/r/c/s/q\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"mounting-a-remote\"\u003eMounting a remote\u003c/h2\u003e\n\u003cp\u003eNow that we have a remote, it would be great to mount it in order to explore the filesystem.\u003c/p\u003e\n\u003cp\u003eThe simplest way is through the following command. Where the mountpoint is an empty directory.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erclone mount remote_name:remote_path /path/to/mountpoint\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo unmount use the \u003ccode\u003efusermount\u003c/code\u003e command.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efusermount -u /path/to/mountpoint\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you forget the exact mountpoint, we can list all mountpoints with the following command.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emount\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can then try to narrow it down such as \u0026ldquo;show me mounts under the /home directory\u0026rdquo;\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emount | grep /home\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ca href=\"https://gist.github.com/kabili207/2cd2d637e5c7617411a666d8d7e97101\"\u003eAmy Nagle wrote the following gist\u003c/a\u003e in order to get this to work with \u003ccode\u003esystemd\u003c/code\u003e. First make sure \u003ccode\u003euser_allow_other\u003c/code\u003e is in \u003ccode\u003e/etc/fuse.conf\u003c/code\u003e.  Then create the following user systemd service in \u003ccode\u003e~/.config/systemd/user/rclone@.service\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003erclone: Remote FUSE filesystem for cloud storage config %i\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDocumentation\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eman:rclone(1)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAfter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enetwork-online.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWants\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enetwork-online.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAssertPathIsDirectory\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e%h/remote/%i\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enotify\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e  /usr/bin/rclone mount \\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    --config=%h/.config/rclone/rclone.conf \\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    --vfs-cache-mode writes \\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    --vfs-cache-max-size 100M \\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    --log-level INFO \\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    --log-file /tmp/rclone-%i.log \\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    --umask 022 \\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    --allow-other \\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    %i: %h/remote/%i\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStop\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/bin/fusermount -u %h/remote/%i\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edefault.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen you can enable it on user login with the following command.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemctl --user enable rclone@REMOTE_NAME\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can start it now by\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemctl --user start rclone@REMOTE_NAME\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"serving-a-remote\"\u003eServing a remote\u003c/h2\u003e\n\u003cp\u003eThis option is especially useful when you want a computer to access a cloud service without having \u003ccode\u003erclone\u003c/code\u003e or the cloud specific tool installed. A computer can act as a hub for all the cloud services and present it to other computers in a network with a standard protocol such as SFTP and WebDAV.\u003c/p\u003e\n\u003cp\u003eTo serve via SFTP:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erclone serve \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t--addr 0.0.0.0:9191 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t--user brandon \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t--pass SuperSecurePasswordHere \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\tsftp \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\tREMOTE_NAME:\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can adapt Amy Nagle\u0026rsquo;s systemd script above to serve SFTP instead:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003erclone: Remote FUSE filesystem for cloud storage config %i\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDocumentation\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eman:rclone(1)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAfter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enetwork-online.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWants\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enetwork-online.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003esimple\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e  /usr/bin/rclone serve \\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    --config=%h/.config/rclone/rclone.conf \\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    --log-level INFO \\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    --log-file /tmp/rclone-Icedrive-Secure.log \\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    --addr 0.0.0.0:9191 \\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    --user brandon \\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    --pass SUPER_SECURE_PASS_HERE \\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    sftp REMOTE_NAME:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edefault.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eThis is just the tip of the iceburg when it comes to Rclone! Another popular feature is automatically \u003ca href=\"https://rclone.org/crypt/\"\u003eencrypting\u003c/a\u003e files uploaded to cloud storage. Once you have everything configured to your liking, you can synchronize the file under \u003ccode\u003e~/.config/rclone/rclone.conf\u003c/code\u003e across all your machines.\u003c/p\u003e\n",
    "date_published": "2022.04.10",
    "tags": ["Backup"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "ee4c7c4d2966"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/permission-denied-writing-privileged-locations/",
    "url": "https://brandonrozek.com/blog/permission-denied-writing-privileged-locations/",
    "title": "Permission Denied: Writing to Privileged Locations",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003ePerhaps you\u0026rsquo;ve tried something like the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;hi\u0026#34;\u003c/span\u003e \u0026gt; /etc/test\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOnly to receive back an error\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ebash: /etc/test: Permission denied\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis is because the \u003ccode\u003esudo\u003c/code\u003e part only applies to the echo command. Bash which is the process that takes the \u003ccode\u003e\u0026quot;hi\u0026quot;\u003c/code\u003e from the \u003ccode\u003eecho\u003c/code\u003e standard out and writes it to \u003ccode\u003e/etc/test\u003c/code\u003e is still under the unprivileged user.\u003c/p\u003e\n\u003cp\u003eInstead consider the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;hi\u0026#34;\u003c/span\u003e | sudo tee /etc/test\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe command \u003ccode\u003etee\u003c/code\u003e takes whatever is in standard in, and writes it to the filename specified. Since we applied \u003ccode\u003esudo \u003c/code\u003e to the \u003ccode\u003etee\u003c/code\u003e command, it now has the necessary permissions to write to a privileged location.\u003c/p\u003e\n",
    "date_published": "2022.04.07",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "7d133254be3f"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/networkx-random-sample-graph/",
    "url": "https://brandonrozek.com/blog/networkx-random-sample-graph/",
    "title": "Networkx Random Sample Graph",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve been working on several algorithms in \u003ccode\u003enetworkx\u003c/code\u003e. In order to speed up testing, especially on large graphs, I\u0026rsquo;ve been randomly sampling portions of the original graph. The best way I\u0026rsquo;ve found to do this is through the following python snippet:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e random\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erandom_sample_edges \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e random\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esample(list(G\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eedges), SAMPLE_SIZE)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eG_sample \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e nx\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eGraph()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eG_sample\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd_edges_from(random_sample_edges)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIt might be tempting to sample the nodes and then grab the subgraph like the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e random\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erandom_nodes \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e random\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esample(list(G\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003enodes), SAMPLE_SIZE)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eG_sample \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e G\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esubgraph(random_nodes)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHowever, only considering the nodes when sampling  makes it highly likely that the subgraph will significantly less edges. This results in a mostly disconnected subgraph and a loss of information. Sampling the edges prevents this issue at the expense of not capturing single nodes not connected to anything else.\u003c/p\u003e\n",
    "date_published": "2022.04.07",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "e5c5330cc9a7"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/non-root-systemd-scripts/",
    "url": "https://brandonrozek.com/blog/non-root-systemd-scripts/",
    "title": "Non-Root Systemd Scripts",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI know of two ways to run systemd services not as root. They both have their pros and cons associated with them.\u003c/p\u003e\n\u003ch3 id=\"1-define-usergroup\"\u003e1. Define User/Group\u003c/h3\u003e\n\u003cp\u003eThe first method is one I have done before in my \u003ca href=\"/blog/composesystemd/\"\u003edocker-compose startup post\u003c/a\u003e. It involves adding the \u003ccode\u003eUser\u003c/code\u003e and \u003ccode\u003eGroup\u003c/code\u003e fields to the \u003ccode\u003eService\u003c/code\u003e component of the Systemd unit file.\u003c/p\u003e\n\u003cp\u003eFilename: \u003ccode\u003e/etc/systemd/system/docker-compose.service\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eDocker Compose Application Service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRequires\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edocker.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAfter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edocker.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eoneshot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eUser\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ebrandonrozek\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eGroup\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ebrandonrozek\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRemainAfterExit\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eyes\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWorkingDirectory\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/home/brandonrozek/docker/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/docker-compose up -d\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStop\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/docker-compose down\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eTimeoutStartSec\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can then start and enable it at boot with\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl start docker-compose\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl enable docker-compose\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"2-user-level-systemd-with-lingering-users\"\u003e2. User-Level Systemd with Lingering Users\u003c/h3\u003e\n\u003cp\u003eAn alternative is to have the service file in the user\u0026rsquo;s systemd config directory and add the \u003ccode\u003e--user\u003c/code\u003e flag during the systemd call.\u003c/p\u003e\n\u003cp\u003eFilename: \u003ccode\u003e/home/brandonrozek/.config/systemd/user/docker-compose.service\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eDocker Compose Application Service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRequires\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edocker.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAfter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edocker.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eoneshot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRemainAfterExit\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eyes\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWorkingDirectory\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/home/brandonrozek/docker/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/docker-compose up -d\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStop\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/docker-compose down\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eTimeoutStartSec\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo start and enable:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemctl --user start docker-compose\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemctl --user enable docker-compose\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAn issue with this approach as it is right now is that the service will only run while the user is logged in. If you want the service to run despite whether or not the user is logged in, then you\u0026rsquo;ll need to mark the user as lingering.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo loginctl enable-linger $USER\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can check if a user already has that property:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo loginctl show-user $USER --property Linger\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can even list all users with the linger property:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003els /var/lib/systemd/linger\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo disable that property:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo loginctl disable-linger $USER\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow what does linger even do?\u003c/p\u003e\n\u003cp\u003eFrom \u003ca href=\"https://www.freedesktop.org/software/systemd/man/loginctl.html\"\u003eFreeDesktop\u003c/a\u003e, if linger is enabled for a sepcific user, then a user manager is spawned for that user at boot which handles the user\u0026rsquo;s services and is kept around even after logouts. If  linger is not enabled, then a user\u0026rsquo;s services will not start at boot and will stop after the user has no sessions running.\u003c/p\u003e\n\u003ch3 id=\"proscons\"\u003ePros/Cons\u003c/h3\u003e\n\u003cp\u003eFor a multi-user system, one can argue that linger is not a good property to have because users can then have a lot of processes spawned and persistent even if they are not using the system. In that case, having an admin individually add systemd unit files using the first approach makes sense.\u003c/p\u003e\n\u003cp\u003eThe second approach has the benefit that the admin only has to set the linger property of the user once, and then the user can create as many systemd unit services as they\u0026rsquo;d like.\u003c/p\u003e\n",
    "date_published": "2022.03.15",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "f527d0f12fb8"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/finding-cuda-errors/",
    "url": "https://brandonrozek.com/blog/finding-cuda-errors/",
    "title": "Finding Cuda Errors",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen cuda fails, it fails silently. To combat this, I have gotten into a habit of checking for a failed status for every cuda memory allocation, kernel execution etc. The following is a C++ macro I wrote that checks if a previous cuda call has failed and then prints the current line and file of the macro.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#define checkCudaError() { if (cudaGetLastError() != cudaSuccess) { printf(\u0026#34;[%s:%d] A previous CUDA call failed.\\n\u0026#34;, __FILE__, __LINE__); }\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI recommend using this macro after every cuda call. For example,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecudaMemcpy(dst, src, size, cudaMemcpyHostToDevice); checkCudaError();\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDuring runtime if the call failed, then you will see:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e[example.cu:14] A previous CUDA call failed.\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNote that it states whether or not a previous cuda call has failed and not where it has failed at. By putting the macro after every cuda call, you can narrow down which call causes the failure to occur.\u003c/p\u003e\n",
    "date_published": "2022.03.08",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "a07f460ce553"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/intensional-logic-extends-first-order/",
    "url": "https://brandonrozek.com/blog/intensional-logic-extends-first-order/",
    "title": "Intensional Logic Extends First Order",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe second brightest object in the sky is known as the morgensteorra (morning star) and æfensteorra (evening star). Later on this object became known as Venus. \u003ca href=\"https://en.wikipedia.org/wiki/Venus_in_culture\"\u003e(Wikipedia)\u003c/a\u003e\n$$\n\\text{morgensteorra} = \\text{æfensteorra} = \\text{venus}\n$$\nGottlob Frege asks in 1892  whether we should make a distinction between a sense and a reference. \u003ca href=\"https://plato.stanford.edu/entries/logic-intensional/#Fre\"\u003e(SEP)\u003c/a\u003e \u003ca href=\"https://en.wikipedia.org/wiki/Sense_and_reference\"\u003e(Wikipedia)\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eOne might be tempted to think that traditional first order logic can handle this. To show how we\u0026rsquo;ll need to extend it, let us think of this problem from a cognitive perspective. Lets say that we have a relation $B$ that stands for belief. Now lets say that an agent has a belief that Venus is the evening star.\n$$\nB(\\text{æfensteorra} = \\text{venus})\n$$\nIn first order logic, we can then deduce the following:\n$$\nB(\\text{morgensteorra} = \\text{venus})\n$$\nBut does that make sense? It is possible to hold a belief that Venus is the evening star while not holding a belief that Venus is the morning star. Therefore, we cannot treat belief as a traditional relation symbol. Issues like these give birth to intensional reasoning and from that modal logic.\u003c/p\u003e\n",
    "date_published": "2022.02.26",
    "tags": ["Logic"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "8e07b68aa8f5"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/junit-script/",
    "url": "https://brandonrozek.com/blog/junit-script/",
    "title": "JUnit Script",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eRunning a JUnit test on the terminal is a little annoying. Here is a quick script to make it easier, make it executable and drop it in \u003ca href=\"/blog/customexec/\"\u003e\u003ccode\u003e~/.local/bin\u003c/code\u003e\u003c/a\u003e for easy use.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/sh\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o errexit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o nounset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o pipefail\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eJUNIT_PATH\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/usr/share/java/junit4.jar\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eshow_usage\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Usage: junit [class file path]\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check argument count\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$#\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -ne \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    show_usage\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ejava -cp \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$JUNIT_PATH\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e:. org.junit.runner.JUnitCore \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReplacing \u003ccode\u003eJUNIT_PATH\u003c/code\u003e with the location of the \u003ccode\u003ejunit4.jar\u003c/code\u003e on your system.\u003c/p\u003e\n",
    "date_published": "2022.02.26",
    "tags": ["Java"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "7ca070e54955"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/proving-loop-invariants/",
    "url": "https://brandonrozek.com/blog/proving-loop-invariants/",
    "title": "Loop Invariants",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLoop invariants are one of the more complicated concepts in Hoare Logic. One might say that we can simply unroll the loop, but there are many loops where we don\u0026rsquo;t know the number of iterations in advanced. Therefore, we often consider what is called the loop invariant. These are statements that are true before, during, and after the loop. A useful loop invariant in combination of the negation of the loop condition can also prove some postcondition. Lets look at a multiplication example\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emul\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e a, \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e b) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e x \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e 0;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e p \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e 0;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e (p \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e b) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        x \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e x \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e a;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        p \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e p \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e 1;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"choosing-the-loop-invariant\"\u003eChoosing the Loop Invariant\u003c/h2\u003e\n\u003cp\u003eA useful postcondition in this case would be \u003ccode\u003ex == a * b\u003c/code\u003e. Which loop invariant would help us prove this? Often when crafting loop invariants I have two pieces of advice:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThe loop invariant should involve some part of the postcondition\u003c/li\u003e\n\u003cli\u003eThe loop invariant should involve the iterator\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eFor the example above, the loop invariant should be \u003ccode\u003ex == a * p\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eIf you are using a verifier like Dafny, there are \u003ca href=\"/blog/dafny-loops/\"\u003eadditional invariants\u003c/a\u003e you\u0026rsquo;ll likely need to add.\u003c/p\u003e\n\u003ch2 id=\"proving-the-loop-invariant\"\u003eProving the Loop Invariant\u003c/h2\u003e\n\u003cp\u003eOften we\u0026rsquo;ll use induction to prove the loop invariant. As a reminder induction has four steps:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eShow the base case holds.\u003c/li\u003e\n\u003cli\u003eState the induction hypothesis.\u003c/li\u003e\n\u003cli\u003eState the propositions from the code.\u003c/li\u003e\n\u003cli\u003eSubstitute (3) into (2) until the inductive step is proved.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eBelow is a proof for the loop invariant \u003ccode\u003ex == a * p\u003c/code\u003e using induction.\u003c/p\u003e\n\u003cp\u003eConsider the base case \u003ccode\u003ex = 0; p = 0\u003c/code\u003e\n$$\n\\begin{align*}\nx \u0026amp;== a * 0 \\\\\n0 \u0026amp;== 0 \\checkmark\n\\end{align*}\n$$\nLet us assume the inductive hypothesis holds. That is, \u003ccode\u003ex_n = a * p_n\u003c/code\u003e. Notice that \u003ccode\u003ea\u003c/code\u003e does not change during the loop so there is no need to add a subscript to it.\u003c/p\u003e\n\u003cp\u003eNow we need to know what \u003ccode\u003ex_n\u003c/code\u003e and \u003ccode\u003ep_n\u003c/code\u003e are at any point during the loop. Looking at the code we come up with the following two propositions:\n$$\np_{n + 1} == p_n + 1 \\\\\nx_{n + 1} == x_n + a\n$$\nThen solving for the nth iteration:\n$$\np_n == p_{n + 1} - 1 \\\\\nx_n == x_{n + 1} - a\n$$\nWe can then plug that into our inductive hypothesis and simplify.\n$$\n\\begin{align*}\nx_n \u0026amp;== a * p_n \\\\\nx_{n + 1} - a \u0026amp;== a * (p_{n + 1} - 1) \\\\\nx_{n + 1} - a \u0026amp;== a * p_{n + 1} - a \\\\\nx_{n + 1} \u0026amp;== a * p_{n + 1} \\checkmark\n\\end{align*}\n$$\nWith that we\u0026rsquo;ve proved using induction that our loop invariant holds.\u003c/p\u003e\n\u003ch2 id=\"proving-the-postcondition\"\u003eProving the Postcondition\u003c/h2\u003e\n\u003cp\u003eTo show that a postcondition holds, we need to show that the loop invariant and the negation of the loop condition imply the postcondition.\n$$\n(x_n == a * p_n) \\wedge \\neg(p \u0026lt; b) \\implies_? x == a * b\n$$\nSimplifying\u0026hellip;\n$$\n(x_n == a * p_n) \\wedge \\neg(p \u0026lt; b) \\\\\n\\implies (x_n == a * p_n) \\wedge (p \\ge b) \\\\\n\\implies (x_n \\ge a * b)\n$$\nWe need another loop invariant to show us that $p$ would never be greater than $b$. That is $0 \\le p \\le b$. That gives us:\n$$\n(x_n == a * p_n) \\wedge (p \\ge b) \\wedge (p \\le b) \\\\\n\\implies (x_n == a * p_n) \\wedge (p == b) \\\\\n\\implies (x_n == a * b) \\checkmark \\\n$$\u003c/p\u003e\n",
    "date_published": "2022.02.26",
    "tags": ["Formal Methods"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "395dbef73491"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/git-partial-clones/",
    "url": "https://brandonrozek.com/blog/git-partial-clones/",
    "title": "Git Partial Clones",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI recently was introduced to \u003ca href=\"https://svnbook.red-bean.com/en/1.8/svn.advanced.sparsedirs.html\"\u003eSparse Directories in SVN\u003c/a\u003e. In SVN, you can initially clone a repository and have it be empty until you checkout the specific files needed. I wondered if I can do the same with \u003ccode\u003egit\u003c/code\u003e. For the \u003cem\u003etl;dr\u003c/em\u003e skip to the conclusion section.\u003c/p\u003e\n\u003cp\u003eAs a benchmark, we\u0026rsquo;re going to reference the size of a cryptographic library I helped author. As a baseline, let\u0026rsquo;s see how big the repository is before adding any flags.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit clone https://github.com/symcollab/cryptosolve\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edu -sh cryptosolve\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e90M cryptosolve\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"using-the---filter-flag\"\u003eUsing the \u003ccode\u003e--filter\u003c/code\u003e flag\u003c/h2\u003e\n\u003cp\u003eWith the \u003ccode\u003efilter\u003c/code\u003e flag, blobs that fall under a specified criteria do not get automatically downloaded during a clone. The blobs do, however, get downloaded whenever its associated files get checked out. By setting the flag to \u003ccode\u003eblob:none\u003c/code\u003e, we are telling git to not download any files initially. Though since the main branch gets checked out by default during a clone, git will still download the blobs associated with the main branch.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit clone --filter\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eblob:none https://github.com/symcollab/cryptosolve\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edu -sh cryptosolve\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e2.1M cryptosolve\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"using-the---no-checkout-flag\"\u003eUsing the \u003ccode\u003e--no-checkout\u003c/code\u003e flag\u003c/h2\u003e\n\u003cp\u003eWe can then improve the last command by adding the \u003ccode\u003eno-checkout\u003c/code\u003e flag. This flag will not construct any of the files in the current branch. If you don\u0026rsquo;t include include the filter flag from before, then there really isn\u0026rsquo;t much of a space savings since all the information is stored in the git database.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit clone --no-checkout https://github.com/symcollab/cryptosolve\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edu -sh cryptosolve\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e89M cryptosolve\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eYou can see that there are no files checked out with a \u003ccode\u003els -a\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e.  ..  .git\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThough with the filter flag, we can see the space savings!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit clone \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t--no-checkout \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t--filter\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eblob:none \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\thttps://github.com/symcollab/cryptosolve\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edu -sh cryptosolve\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e508K cryptosolve\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"using-the---sparse-flag\"\u003eUsing the \u003ccode\u003e--sparse\u003c/code\u003e flag\u003c/h2\u003e\n\u003cp\u003eThe sparse flag makes it so that when we checkout a reference, only the immediate files in the root directory are constructed. Additional commands then need to be issued in order to checkout other directories. With the sparse flag by itself, there isn\u0026rsquo;t much savings since all the information is still downloaded and stored in the git database.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit clone --sparse https://github.com/symcollab/cryptosolve\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edu -sh cryptosolve\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e89M cryptosolve\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eThe power comes from when we combine all these flags together.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit clone \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t--filter\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eblob:none \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t--sparse \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t--no-checkout \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\thttps://github.com/symcollab/cryptosolve\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edu -sh cryptosolve\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e516K cryptosolve\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIt\u0026rsquo;s not different from the \u003ccode\u003e--filter=blob:none --no-checkout\u003c/code\u003e command initially, but when we checkout a branch we can see that not all the blobs get downloaded.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd cryptosolve\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit checkout main\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd ..\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edu -sh cryptosolve\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e644K cryptosolve\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eYou can fetch folders as you please with the following command:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit sparse-checkout add FOLDERNAME\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can even set it so that a specific folder is shown at the root of your directory.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit sparse-checkout set FOLDERNAME\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.02.07",
    "tags": ["Git"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "95d4bd60af72"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/dafny-loops/",
    "url": "https://brandonrozek.com/blog/dafny-loops/",
    "title": "Reasoning through Loops in Dafny",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eDafny treats loops like a black box. It could be annoying the first time you experience this and have no clue why the code is not verifying properly. There are two properties that Dafny needs you to prove: partial correctness and termination. Together these form \u003cem\u003etotal correctness\u003c/em\u003e.\u003c/p\u003e\n\u003ch2 id=\"partial-correctness\"\u003ePartial Correctness\u003c/h2\u003e\n\u003cp\u003ePartial correctness is concerned with whether the system is sound and complete. In other words, if the loop terminates that the postcondition holds for all possible inputs constrained by the precondition.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emethod arrayUpToN(n: \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e) returns (a: array\u0026lt;\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e\u0026gt;)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    requires n \u0026gt;= \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ensures a.Length == n\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ensures forall j :: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt; j \u0026lt; n ==\u0026gt; a[j] \u0026gt;= \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ensures forall j, k : \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e :: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt;= j \u0026lt;= k \u0026lt; n ==\u0026gt; a[j] \u0026lt;= a[k]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e i := \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    a := \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e[n];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e i \u0026lt; n\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        a[i] := i;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        i := i + \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn order to solve this, we need to concept of a loop invariant. A loop invariant is a formula that is true before, during, and after the loop. In order to prove the postcondition, we need to choose the relevant loop invariants such that:\n$$\n\\neg Loop_Condition \\wedge Loop_Invariants \\implies Post_Condition\n$$\nIf this is not the case, then Dafny will likely complain to you as it has no clue what\u0026rsquo;s going on. I generally always first add bounds to the iterator and other variables.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003einvariant \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt;= i \u0026lt;= n\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHere are the other invariants you\u0026rsquo;ll need in order for the postcondition in the example to verify:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003einvariant forall k :: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt;= k \u0026lt; i ==\u0026gt; a[k] \u0026gt;= \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003einvariant forall k :: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt;= k \u0026lt; i ==\u0026gt; a[k] == k\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003einvariant forall j, k :: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt;= j \u0026lt;= k \u0026lt; i ==\u0026gt; a[j] \u0026lt;= a[k]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTogether this forms:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emethod arrayUpToN(n: \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e) returns (a: array\u0026lt;\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e\u0026gt;)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    requires n \u0026gt;= \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ensures a.Length == n\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ensures forall j :: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt; j \u0026lt; n ==\u0026gt; a[j] \u0026gt;= \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ensures forall j, k : \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e :: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt;= j \u0026lt;= k \u0026lt; n ==\u0026gt; a[j] \u0026lt;= a[k]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e i := \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    a := \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e[n];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e i \u0026lt; n\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        invariant \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt;= i \u0026lt;= n\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        invariant forall k :: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt;= k \u0026lt; i ==\u0026gt; a[k] \u0026gt;= \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        invariant forall k :: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt;= k \u0026lt; i ==\u0026gt; a[k] == k\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        invariant forall j, k :: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt;= j \u0026lt;= k \u0026lt; i ==\u0026gt; a[j] \u0026lt;= a[k]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        a[i] := i;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        i := i + \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"termination\"\u003eTermination\u003c/h2\u003e\n\u003cp\u003eTermination is concerned with whether your loop will finish in finite time. The way that Dafny does this is by guessing what is called the Decrementing function. In Dafny, the decrementing function is required to:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eDecrease with each iteration of the loop\u003c/li\u003e\n\u003cli\u003eBe bounded below by zero\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIn the last example, Dafny was able to correctly guess that decrementing function was $n - i$. What about the following example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emethod fact(x: \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e) returns (y: \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    requires x \u0026gt;= \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e z := \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    y := \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e z != x\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        invariant \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt;= z \u0026lt;= x\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        z := z + \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        y := y * z;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTurns out in this scenario, Dafny was not able to successfully guess. To supply it with the decrementing function use the keyword \u003ccode\u003edecreases\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emethod fact(x: \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e) returns (y: \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    requires x \u0026gt;= \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e z := \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    y := \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e z != x\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        decreases x - z\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        invariant \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt;= z \u0026lt;= x\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        z := z + \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        y := y * z;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.02.05",
    "tags": ["Formal Methods"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "c0e16f8cd368"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/program-verification-hoare-logic-dafny/",
    "url": "https://brandonrozek.com/blog/program-verification-hoare-logic-dafny/",
    "title": "Program Verification with Hoare Logic and Dafny",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn the recitations that I\u0026rsquo;m giving for \u003ca href=\"/ta/spring2022/csci2600/\"\u003ePrinciples of Software\u003c/a\u003e, we are going over reasoning through code using Hoare Logic and the program verifier Dafny. Microsoft Research designed Dafny to be similar to writing imperative code. The main difference is that you need to supply (pre/post)-conditions and the code to verify. Here\u0026rsquo;s an example of how to reason about a simple statement by hand:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Precondition: x \u0026gt; 0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ex := x + \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Postcondition: x \u0026gt; 1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eGiven the fact that x is greater than zero before the code begins, we can deduce that if we increment it by one then it must be greater than one! Though to showcase Dafny, we\u0026rsquo;ll look at a slightly more interesting (albeit famous beginner example) which is the absolute value:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emethod abs(x: \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e) returns (y: \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    requires \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ensures y \u0026gt;= \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ensures x \u0026gt;= \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e ==\u0026gt; x == y\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (x \u0026lt; \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        y := -x;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    } \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        y := x;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003erequires\u003c/code\u003e clause is otherwise known as the precondition or what is known to be true before the method executes. For the case of absolute value, we do not need to constrain any of the integer inputs, so we can put \u003ccode\u003erequires true\u003c/code\u003e or not even include that line.\u003c/p\u003e\n\u003cp\u003eThe \u003ccode\u003eensures\u003c/code\u003e clause is otherwise known as the postcondition or what you want verified to be true after the method executes. In there we have two conditions: the output must be positive, and if the input was positive then the output equals the input.\u003c/p\u003e\n\u003cp\u003eThen finally the code goes in the method body.\u003c/p\u003e\n\u003cp\u003eNow you can actually verify it line by line through the use of \u003ccode\u003eassert\u003c/code\u003e statements. Here\u0026rsquo;s the same method again but with assertions:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emethod abs(x: \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e) returns (y: \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    requires \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ensures y \u0026gt;= \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ensures x \u0026gt;= \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e ==\u0026gt; x == y\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (x \u0026lt; \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        assert x \u0026lt; \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        y := -x;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        assert y \u0026gt; x;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    } \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        assert x \u0026gt;= \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        y := x;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        assert y == x;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.02.05",
    "tags": ["Formal Methods"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "4cf20da7d8a4"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/docker-secrets/",
    "url": "https://brandonrozek.com/blog/docker-secrets/",
    "title": "Docker Secrets",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI try to keep secrets such as passwords and keys out in their own separate files so that I can \u003ccode\u003e.gitignore\u003c/code\u003e them and commit the rest of my configuration. With \u003ccode\u003edocker-compose\u003c/code\u003e we can do that with the \u003ccode\u003eenv_file\u003c/code\u003e field. Here is an example with a postgres configuration:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003edatabase\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eimage\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003epostgres:13.4\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003econtainer_name\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003edatabase\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003ehostname\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003edatabase\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eenv_file\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003eVolumes/database/docker.env\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003evolumes\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003eVolumes/database/var/lib/postgresql/data:/var/lib/postgresql/data\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen in \u003ccode\u003eVolumes/database/docker.env\u003c/code\u003e I can have a file with the secrets as key-value pairs:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003ePOSTGRES_USER=user\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003ePOSTGRES_PASSWORD=389ed93045c84cc0828c4310e6ef76ce\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003ePOSTGRES_DB=database\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.02.04",
    "tags": ["Containers"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "58cc9e00b1a7"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/s3-fuse/",
    "url": "https://brandonrozek.com/blog/s3-fuse/",
    "title": "Mount Object Storage Locally using S3 Fuse",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOn most cloud providers, object storage is cheaper than paying for the equivalent size in block storage. Using FUSE, we can mount S3 compatible object storage with the command \u003ccode\u003es3fs\u003c/code\u003e. Do note, that there are a few downsides with mounting object storage as documented on their \u003ca href=\"https://github.com/s3fs-fuse/s3fs-fuse/blob/master/README.md\"\u003eREADME\u003c/a\u003e:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003erandom writes or appends to files require rewriting the entire object, optimized with multi-part upload copy\u003c/li\u003e\n\u003cli\u003emetadata operations such as listing directories have poor performance due to network latency\u003c/li\u003e\n\u003cli\u003enon-AWS providers may have eventual consistency so reads can temporarily yield stale data (AWS offers read-after-write consistency since Dec 2020)\u003c/li\u003e\n\u003cli\u003eno atomic renames of files or directories\u003c/li\u003e\n\u003cli\u003eno coordination between multiple clients mounting the same bucket\u003c/li\u003e\n\u003cli\u003eno hard links\u003c/li\u003e\n\u003cli\u003einotify detects only local modifications, not external ones by other clients or tools\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eLets get started by installing \u003ccode\u003es3fs\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# For Fedora\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dnf install s3fs-fuse\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# For Ubuntu/Debian\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install s3fs\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe\u0026rsquo;ll then need to edit \u003ccode\u003e/etc/passwd-s3fs\u003c/code\u003e with our object storage access and secret keys:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eAccessKeyHere:SecretKeyHere\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen we need to set it so that only root can read \u003ccode\u003e/etc/passwd-s3fs\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo chmod \u003cspan style=\"color:#ae81ff\"\u003e600\u003c/span\u003e /etc/passwd-s3fs\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow we can test to see if we can access our bucket:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo s3fs bucketname \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t/mnt/mountpoint \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t-o url\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ehttps://us-east-1.linodeobjects.com \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t-o allow_other\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf we\u0026rsquo;re successful we should be able to access \u003ccode\u003e/mnt/mountpoint\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eTo unmount:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo umount /mnt/mountpoint\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo mount automatically during reboot, add the following to \u003ccode\u003e/etc/fstab\u003c/code\u003e:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ebucketname /mnt/mountpoint     fuse.s3fs _netdev,allow_other,url=https://us-east-1.linodeobjects.com  0 0\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAfter editing \u003ccode\u003e/etc/fstab\u003c/code\u003e you can run \u003ccode\u003esudo mount -a\u003c/code\u003e in order for it to load and mount any new entries.\u003c/p\u003e\n",
    "date_published": "2022.02.04",
    "tags": ["Storage"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "a6add7081378"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/git-diff-by-words/",
    "url": "https://brandonrozek.com/blog/git-diff-by-words/",
    "title": "Git Diff by Words",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen working in LaTex it is often more useful to see the changes by words as opposed to lines. Luckily, it\u0026rsquo;s pretty easy to view this in the terminal.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit diff --color-words\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.02.04",
    "tags": ["Git"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "1fa7eb8ae0f5"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/snap-flatpak-aliases/",
    "url": "https://brandonrozek.com/blog/snap-flatpak-aliases/",
    "title": "Aliases for Snaps and Flatpaks",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eSometimes snap packages and flatpaks come with unmemorable names to run their commands. Luckily for snap packages, you can easily alias them. For example, to rename the .NET executable to \u003ccode\u003edotnet\u003c/code\u003e run.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo snap alias dotnet-sdk.dotnet dotnet\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSadly the same cannot be said for Flatpaks. There is a \u003ca href=\"https://github.com/flatpak/flatpak/issues/1565\"\u003eGithub issue\u003c/a\u003e open at the moment. I\u0026rsquo;ll update this post when they successfully close the issue. In the meantime, we can use a script made by Seth Kenlon over at \u003ca href=\"https://www.redhat.com/sysadmin/launch-flatpaks-terminal-fuzzpak\"\u003eRed Hat\u0026rsquo;s blog\u003c/a\u003e. The script is at the bottom of the post for posterity. You can save it to \u003ccode\u003e~/.local/bin\u003c/code\u003e, \u003ccode\u003echmod +x ~/.local/bin\u003c/code\u003e, and then run \u003ccode\u003efuzzpak [appname]\u003c/code\u003e where \u003ccode\u003eappname\u003c/code\u003e is the last part of the flatpak name. For example, the package\u003ccode\u003eio.typora.Typora\u003c/code\u003e can be called with \u003ccode\u003efuzzpak typora\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# parse opts\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e True \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;--help\u0026#34;\u003c/span\u003e -o \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;-h\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$0\u003cspan style=\"color:#e6db74\"\u003e [OPTIONS]\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;--directory, -d   Location of flatpaks (default: \u003c/span\u003e$HOME\u003cspan style=\"color:#e6db74\"\u003e/.var/app\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eelif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;--directory\u0026#34;\u003c/span\u003e -o \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;-d\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DIR\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$DIR\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    shift \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    break\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# main\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elaunch_app \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003e1\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.02.04",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "5f51916cafd3"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/nginx-gzip/",
    "url": "https://brandonrozek.com/blog/nginx-gzip/",
    "title": "Reducing Network Bandwidth in Nginx with Gzip",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eBrowsers that support gzip compression send the header \u003ccode\u003eAccept-Encoding: gzip\u003c/code\u003e in its request and if the webserver supports gzip, then it can send the website data back compressed. Though recently I learned that this isn\u0026rsquo;t setup by default in \u003ccode\u003enginx\u003c/code\u003e! This post will go over the configuration you\u0026rsquo;ll need to add to \u003ccode\u003e/etc/nginx/nginx.conf\u003c/code\u003e in order to support \u003ccode\u003egzip\u003c/code\u003e compression. From my experience, enabling \u003ccode\u003egzip\u003c/code\u003e compression reduced network traffic by 1/4.\u003c/p\u003e\n\u003cp\u003eInside of the \u003ccode\u003eserver {}\u003c/code\u003e block in \u003ccode\u003e/etc/nginx/nginx.conf\u003c/code\u003e add the following lines:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003egzip\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eon\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003egzip_disable\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;msie6\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003egzip_vary\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eon\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003egzip_proxied\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eno-cache\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eno-store\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eprivate\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eexpired\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eauth\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003egzip_comp_level\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e6\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003egzip_buffers\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e16\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e8k\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003egzip_http_version\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e.1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003egzip_min_length\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1024\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003egzip_types\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#e6db74\"\u003eapplication/atom+xml\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#e6db74\"\u003eapplication/geo+json\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eapplication/javascript\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eapplication/x-javascript\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eapplication/json\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eapplication/ld+json\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eapplication/manifest+json\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eapplication/rdf+xml\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eapplication/rss+xml\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eapplication/xhtml+xml\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eapplication/xml\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003efont/eot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003efont/otf\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003efont/ttf\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003eimage/svg+xml\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003etext/css\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003etext/javascript\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003etext/plain\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003etext/xml\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhat each of the commands do:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eCommand\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003egzip_disable\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eDisables gzipping of responses for requests with “User-Agent” header fields matching any of the specified regular expressions.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003egzip_vary\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eEnables or disables inserting the “Vary: Accept-Encoding” response header field if the directives \u003ca href=\"https://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip\"\u003egzip\u003c/a\u003e, \u003ca href=\"https://nginx.org/en/docs/http/ngx_http_gzip_static_module.html#gzip_static\"\u003egzip_static\u003c/a\u003e, or \u003ca href=\"https://nginx.org/en/docs/http/ngx_http_gunzip_module.html#gunzip\"\u003egunzip\u003c/a\u003e are active.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003egzip_proxied\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eEnables or disables gzipping of responses for proxied requests depending on the request and response. The fact that the request is proxied is determined by the presence of the “Via” request header field.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003egzip_comp_level\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eSets a gzip compression \u003ccode\u003e*level*\u003c/code\u003e of a response. Acceptable values are in the range from 1 to 9.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003egzip_buffers\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eSets the \u003ccode\u003e*number*\u003c/code\u003e and \u003ccode\u003e*size*\u003c/code\u003e of buffers used to compress a response. By default, the buffer size is equal to one memory page. This is either 4K or 8K, depending on a platform.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003egzip_http_version\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eSets the minimum HTTP version of a request required to compress a response.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003egzip_min_length\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eSets the minimum length of a response that will be gzipped. The length is determined only from the “Content-Length” response header field.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003egzip_types\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eEnables gzipping of responses for the specified MIME types in addition to “\u003ccode\u003etext/html\u003c/code\u003e”. The special value “\u003ccode\u003e*\u003c/code\u003e” matches any MIME type (0.8.29). Responses with the “\u003ccode\u003etext/html\u003c/code\u003e” type are always compressed.\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n",
    "date_published": "2022.02.04",
    "tags": ["Nginx"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "ebd510dbf691"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/dafny-3-3-countermodel/",
    "url": "https://brandonrozek.com/blog/dafny-3-3-countermodel/",
    "title": "Dafny v3.3 Show Countermodel",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cem\u003eWarning: \u003ccode\u003eextractcounterexample\u003c/code\u003e is a new flag in Dafny and the command to extract it will likely change\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.microsoft.com/en-us/research/project/dafny-a-language-and-program-verifier-for-functional-correctness/\"\u003eDafny\u003c/a\u003e is a program verifier made by Microsoft. The programmer specifies the pre and post conditions and Dafny will try to verify that the codebase meets those specifications. One of its coolest features is that Dafny will can provide a countermodel for why it things the code does not verify.  Lets look at an example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emethod arrayUpToN(n: \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e) returns (a: array\u0026lt;\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e\u0026gt;)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    requires n \u0026gt;= \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ensures a.Length == n\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ensures forall j :: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt; j \u0026lt; n ==\u0026gt; a[j] \u0026gt;= \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ensures forall j, k : \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e :: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt;= j \u0026lt;= k \u0026lt; n ==\u0026gt; a[j] \u0026lt;= a[k]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e i := \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    a := \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e[n];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e i \u0026lt; n\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        invariant \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt;= i \u0026lt;= n\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        invariant forall k :: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt;= k \u0026lt; i ==\u0026gt; a[k] \u0026gt;= \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        invariant forall j, k :: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt;= j \u0026lt;= k \u0026lt; i ==\u0026gt; a[j] \u0026lt;= a[k]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        a[i] := i;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        i := i + \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSay the content above is saved in  \u003ccode\u003eexample.dfy\u003c/code\u003e. To verify it and show a countermodel if it fails:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edafny example.dfy \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t/compile:4 /verifyAllModules \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t/proverOpt:O:model_compress\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003efalse \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t/proverOpt:O:model.completion\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003etrue \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t/warnShadowing /extractCounterexample \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t/errorTrace:0 /mv:example.model\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe output can be a bit cryptic:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eexample.dfy(14,18): Error: This loop invariant might not be maintained by the loop.\nexample.dfy(14,18): Related message: loop invariant violation\n\nDafny program verifier finished with 1 verified, 1 error\nCompiled assembly into example.dll\nProgram compiled successfully\nCounterexample for first failing assertion: \nexample.dfy(7,0): initial state:\n        a : _System.array?\u0026lt;int\u0026gt; = ()\n        n : int = 1237\n        j : int = 196\n        k : int = 1236\nexample.dfy(8,14):\n        a : _System.array?\u0026lt;int\u0026gt; = ()\n        n : int = 1237\n        i : int = 0\n        j : int = 196\n        k : int = 1236\nexample.dfy(9,19):\n        a : _System.array?\u0026lt;int\u0026gt; = (Length := 1237, [1236] := 1236, [196] := 1686)\n        n : int = 1237\n        i : int = 0\n        j : int = 196\n        k : int = 1236\nexample.dfy(10,4): after some loop iterations:\n        a : _System.array?\u0026lt;int\u0026gt; = (Length := 1237, [196] := 1686)\n        n : int = 1237\n        i : int = k\n        j : int = 196\n        k : int = 1236\nexample.dfy(16,17):\n        a : _System.array?\u0026lt;int\u0026gt; = (Length := 1237, [1236] := 1236, [196] := 1686)\n        n : int = 1237\n        i : int = k\n        j : int = 196\n        k : int = 1236\nexample.dfy(17,18):\n        a : _System.array?\u0026lt;int\u0026gt; = (Length := 1237, [1236] := 1236, [196] := 1686)\n        n : int = 1237\n        i : int = n\n        j : int = 196\n        k : int = 1236\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe important thing to note here is that it thinks \u003ccode\u003ea[196] = 1686\u003c/code\u003e which is not true at all given the code. Turns out, we\u0026rsquo;ll have to add an extra loop invariant in order for it to verify correctly:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003einvariant forall k :: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u0026lt;= k \u0026lt; i ==\u0026gt; a[k] == k\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRunning Dafny again will produce:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eDafny program verifier finished with 2 verified, 0 errors\nCompiled assembly into example3.dll\nProgram compiled successfully\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2022.02.04",
    "tags": ["Formal Methods"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "b1976dcfde09"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/rebuildkernelakmod/",
    "url": "https://brandonrozek.com/blog/rebuildkernelakmod/",
    "title": "Rebuild Kernel Modules with Akmods",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eAkmods is the Fedora/Red Hat way of managing kernel modules. In Ubuntu, this is \u003ccode\u003edkms\u003c/code\u003e. If you\u0026rsquo;re like me and force reboot shortly after performing an update, then you might have not given akmods enough time to compile any extra kernel modules (for example: Nvidia). This meant that I had to boot into an older kernel to try to fix the problem\u0026hellip;.\u003c/p\u003e\n\u003cp\u003eOnce in the older kernel, you can check the kernel versions by:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003els /usr/src/kernels/\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen select the kernel which you failed to build and run:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo akmods --kernels 5.15.18-200.fc35.x86_64\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eto trigger the rebuild.\u003c/p\u003e\n\u003cp\u003eThough the better solution is to avoid this problem to begin with.\nIf you \u003ccode\u003ereboot\u003c/code\u003e not as root, then systemd will check to see if\nany process is inhibiting the poweroff. If that\u0026rsquo;s the case,\nwait patiently and don\u0026rsquo;t type \u003ccode\u003esudo reboot\u003c/code\u003e.\u003c/p\u003e\n",
    "date_published": "2022.02.04",
    "tags": ["Linux","Fedora"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "ab0e2a2ce3b3"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/rootless-docker-compose-podman/",
    "url": "https://brandonrozek.com/blog/rootless-docker-compose-podman/",
    "title": "Rootless Docker-Compose with Podman",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cem\u003eNote: Nowadays, I prefer to use \u003ca href=\"/blog/migrating-docker-compose-podman-quadlets/\"\u003eQuadlets\u003c/a\u003e\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eOne of the benefits of Podman over Docker is that it can run daemon-less and without root. However, \u003ccode\u003edocker-compose\u003c/code\u003e is by far my favorite way to create and maintain containers. Luckily, the Podman folks emulated the Docker CLI so that \u003ccode\u003edocker-compose\u003c/code\u003e works well with Podman!\u003c/p\u003e\n\u003cp\u003eTo install:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dnf install -y podman podman-docker docker-compose\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can then emulate the docker socket rootless with the following commands:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemctl --user enable podman.socket\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemctl --user start podman.socket\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAt this point, we\u0026rsquo;ll want to see if the daemon acts as expected\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl -H \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Content-Type: application/json\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e\t--unix-socket /var/run/user/$UID/podman/podman.sock \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e    http://localhost/_ping\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis should return \u003ccode\u003eOK\u003c/code\u003e. We then need to create an environmental variable to tell docker compose where the emulated docker socket lives.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport DOCKER_HOST\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eunix:///run/user/$UID/podman/podman.sock\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo have this environmental variable persistent across reboots, add the above line to the user\u0026rsquo;s \u003ccode\u003e.bash_profile\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eYou\u0026rsquo;ll need a configuration file \u003ccode\u003edocker-compose.yml\u003c/code\u003e defined. Here is a sample one that spins up an image updating service. Replace \u003ccode\u003e$UID\u003c/code\u003e with your user id which you can get from running \u003ccode\u003eid -u\u003c/code\u003e in the terminal.\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eversion\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;3.3\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eservices\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003ewatchtower\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eimage\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003edocker.io/containrrr/watchtower \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003econtainer_name\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ewatchtower\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ehostname\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ewatchtower\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eenvironment\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003ePUID\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003ePGID\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003eTZ\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eUS/Eastern\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003evolumes\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003e/var/run/user/$UID/podman/podman.sock:/var/run/docker.sock:ro\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003erestart\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ealways\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you want to add to add more volumes to the container, make sure it has the appropriate SELinux label if\nyou\u0026rsquo;re using a distribution with it enabled.\u003csup id=\"fnref:2\"\u003e\u003ca href=\"#fn:2\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e2\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echcon -t container_file_t -R X\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003ewhere \u003ccode\u003eX\u003c/code\u003e is the volume you wish to mount.\u003c/p\u003e\n\u003cp\u003eNow we can run \u003ccode\u003edocker-compose\u003c/code\u003e!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker-compose ps\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eThanks to Ian Evans for sending in a correction to the volumes declaration.\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli id=\"fn:2\"\u003e\n\u003cp\u003e\u003ca href=\"https://bugzilla.redhat.com/show_bug.cgi?id=2125878\"\u003ehttps://bugzilla.redhat.com/show_bug.cgi?id=2125878\u003c/a\u003e\u0026#160;\u003ca href=\"#fnref:2\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2022.01.29",
    "tags": ["Containers"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "d31b7e2a688c"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/codeoutputlatex/",
    "url": "https://brandonrozek.com/blog/codeoutputlatex/",
    "title": "Code alongside Output in LaTex",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWorking on a tool paper, I had the desire to share code alongside its output. With a combination of the \u003ccode\u003elistings\u003c/code\u003e, \u003ccode\u003ecaption\u003c/code\u003e, and \u003ccode\u003ecolor\u003c/code\u003e packages, we can get a result that\u0026rsquo;s nice looking.\u003c/p\u003e\n\u003cp\u003eThe imports needed\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usepackage\u003c/span\u003e{color}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usepackage\u003c/span\u003e{listings}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usepackage\u003c/span\u003e{caption}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI want a think topbar that labels which column is the code section and which column is the output section.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\DeclareCaptionFormat\u003c/span\u003e{listing}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\u003cspan style=\"color:#66d9ef\"\u003e\\colorbox\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[cmyk]\u003c/span\u003e{0.73, 0.35, 0.15,.5}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\u003cspan style=\"color:#66d9ef\"\u003e\\parbox\u003c/span\u003e{\u003cspan style=\"color:#66d9ef\"\u003e\\textwidth\u003c/span\u003e}{#1#2#3}}}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\DeclareCaptionFont\u003c/span\u003e{white}{\u003cspan style=\"color:#66d9ef\"\u003e\\color\u003c/span\u003e{white}}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\captionsetup\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[lstlisting]\u003c/span\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    format=listing,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    labelfont=white,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    textfont=white,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    font={bf,footnotesize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNext I define some colors that I would like to use as the theme for the code output:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\definecolor\u003c/span\u003e{keyblue}{rgb}{0.1, 0.1, 0.6}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\definecolor\u003c/span\u003e{dkgreen}{rgb}{0,0.6,0}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\definecolor\u003c/span\u003e{gray}{rgb}{0.5,0.5,0.5}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\definecolor\u003c/span\u003e{stringcol}{rgb}{0.58,0.4,0.1}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSetup for the code output\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\lstset\u003c/span\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  language=Python,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  columns=flexible,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  basicstyle={\u003cspan style=\"color:#66d9ef\"\u003e\\small\\ttfamily\u003c/span\u003e},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  keywordstyle=\u003cspan style=\"color:#66d9ef\"\u003e\\color\u003c/span\u003e{keyblue},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  commentstyle=\u003cspan style=\"color:#66d9ef\"\u003e\\color\u003c/span\u003e{dkgreen},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  stringstyle=\u003cspan style=\"color:#66d9ef\"\u003e\\color\u003c/span\u003e{stringcol},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  breaklines=true,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  breakatwhitespace=true,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  tabsize=4\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen inside the document, you can use \u003ccode\u003eminipage\u003c/code\u003e to setup a two column layout.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e% Column 1: Code\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{minipage}[t]{.45\u003cspan style=\"color:#66d9ef\"\u003e\\textwidth\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{lstlisting}[title=Code]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edef factorial(n):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    if n == 0:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        return 1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    return n * factorial(n - 1)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e# Let\u0026#39;s test it out!\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(factorial(5))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{lstlisting}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{minipage}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e% Column 2: Output\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{minipage}[t]{.45\u003cspan style=\"color:#66d9ef\"\u003e\\textwidth\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{lstlisting}[title=Output]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    120\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{lstlisting}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{minipage}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/202201292013.svg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eBelow is all these steps combined into a compile-able LaTex file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\documentclass\u003c/span\u003e{article}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usepackage\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[utf8]\u003c/span\u003e{inputenc}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e% Imports\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usepackage\u003c/span\u003e{color}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usepackage\u003c/span\u003e{listings}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usepackage\u003c/span\u003e{caption}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e% Define colors\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\definecolor\u003c/span\u003e{keyblue}{rgb}{0.1, 0.1, 0.6}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\definecolor\u003c/span\u003e{dkgreen}{rgb}{0,0.6,0}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\definecolor\u003c/span\u003e{gray}{rgb}{0.5,0.5,0.5}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\definecolor\u003c/span\u003e{stringcol}{rgb}{0.58,0.4,0.1}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e% Caption Setup\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\DeclareCaptionFormat\u003c/span\u003e{listing}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\u003cspan style=\"color:#66d9ef\"\u003e\\colorbox\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[cmyk]\u003c/span\u003e{0.73, 0.35, 0.15,.5}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\u003cspan style=\"color:#66d9ef\"\u003e\\parbox\u003c/span\u003e{\u003cspan style=\"color:#66d9ef\"\u003e\\textwidth\u003c/span\u003e}{#1#2#3}}}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\DeclareCaptionFont\u003c/span\u003e{white}{\u003cspan style=\"color:#66d9ef\"\u003e\\color\u003c/span\u003e{white}}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\captionsetup\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[lstlisting]\u003c/span\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    format=listing,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    labelfont=white,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    textfont=white,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    font={bf,footnotesize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e% Code listing setup\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\lstset\u003c/span\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  language=Python,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  columns=flexible,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  basicstyle={\u003cspan style=\"color:#66d9ef\"\u003e\\small\\ttfamily\u003c/span\u003e},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  keywordstyle=\u003cspan style=\"color:#66d9ef\"\u003e\\color\u003c/span\u003e{keyblue},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  commentstyle=\u003cspan style=\"color:#66d9ef\"\u003e\\color\u003c/span\u003e{dkgreen},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  stringstyle=\u003cspan style=\"color:#66d9ef\"\u003e\\color\u003c/span\u003e{stringcol},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  breaklines=true,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  breakatwhitespace=true,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  tabsize=4\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{document}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e% Column 1: Code\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{minipage}[t]{.45\u003cspan style=\"color:#66d9ef\"\u003e\\textwidth\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{lstlisting}[title=Code]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edef factorial(n):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    if n == 0:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        return 1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    return n * factorial(n - 1)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e# Let\u0026#39;s test it out!\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(factorial(5))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{lstlisting}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{minipage}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e% Column 2: Output\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{minipage}[t]{.45\u003cspan style=\"color:#66d9ef\"\u003e\\textwidth\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{lstlisting}[title=Output]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    120\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{lstlisting}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{minipage}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{document}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.01.27",
    "tags": ["LaTex"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "c4d061953056"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/multicolumnbeamerslide/",
    "url": "https://brandonrozek.com/blog/multicolumnbeamerslide/",
    "title": "Multi-Column slides in Beamer",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen creating slides in LaTex Beamer, it can be frustrating to not have the easy ability to drag text boxes around. Luckily, creating a multi-column layout in Beamer is not difficult! Below is an example of a two column slide layout, where the left side is a bulleted list, and the right side is an image:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\documentclass\u003c/span\u003e{beamer}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usepackage\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[utf8]\u003c/span\u003e{inputenc}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e% Beamer Packages\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usepackage\u003c/span\u003e{harvard}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usetheme\u003c/span\u003e{Copenhagen}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{document}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{frame}{About Me}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{columns}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e% Column 1\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\column\u003c/span\u003e{.5\u003cspan style=\"color:#66d9ef\"\u003e\\linewidth\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{itemize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e Enjoy going on short hikes.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e Forgetful at times and writes blog posts to jog my memory.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{itemize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e% Column 2\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\column\u003c/span\u003e{.5\u003cspan style=\"color:#66d9ef\"\u003e\\linewidth\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\centering\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\includegraphics\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[width=4cm]\u003c/span\u003e{images/avatar.jpg}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{columns}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{frame}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{document}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/202201262338.svg\" alt=\"\"\u003e\u003c/p\u003e\n",
    "date_published": "2022.01.26",
    "tags": ["LaTex"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "cd19b12d502f"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/codeinbeamer/",
    "url": "https://brandonrozek.com/blog/codeinbeamer/",
    "title": "Code in LaTex Beamer",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI commonly use the \u003ca href=\"/blog/latexcode/\"\u003e\u003ccode\u003elistings\u003c/code\u003e package\u003c/a\u003e to showcase code in my LaTex documents. I tried doing the same in my Beamer slidedecks and I ran into an issue where the LaTex source code failed to compile. After digging around, I figured out its because every slide or frame that includes code (or any verbatim environment) needs to be marked as \u003ccode\u003efragile\u003c/code\u003e. A minimal example is presented below:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\documentclass\u003c/span\u003e{beamer}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usepackage\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[utf8]\u003c/span\u003e{inputenc}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e% Beamer Packages\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usepackage\u003c/span\u003e{harvard}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usetheme\u003c/span\u003e{Copenhagen}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e% Code Rendering Packages\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usepackage\u003c/span\u003e{listings}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\lstset\u003c/span\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  language=Java,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  columns=flexible,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e% Begin Slidedeck\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{document}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{frame}[fragile]{Code Example}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{lstlisting}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    int x = 5;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{lstlisting}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{frame}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{document}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2022.01.26",
    "tags": ["LaTex"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "d91f704bb53a"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/termtosvg/",
    "url": "https://brandonrozek.com/blog/termtosvg/",
    "title": "Terminal Output To SVG Animations",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWith \u003ca href=\"https://github.com/nbedos/termtosvg\"\u003e\u003ccode\u003etermtosvg\u003c/code\u003e\u003c/a\u003e made by Nicolas Bedo we can make SVG animations from terminal output in the style of  \u003ca href=\"https://asciinema.org/\"\u003easciinema\u003c/a\u003e. To install use \u003ca href=\"/blog/managepythonapps/\"\u003epipx\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epipx install termtosvg\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo start recording, run the command \u003ccode\u003etermtosvg\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etermtosvg\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIt first outputs:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eRecording started, enter \u0026#34;exit\u0026#34; command or Control-D to end\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAt \u0026ldquo;exit\u0026rdquo;, by default it will save the animation to a random filename in the tmp folder.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eRendering ended, SVG animation is /tmp/termtosvg_xmadgf9y.svg\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTo control the default save location, pass in a filename after \u003ccode\u003etermtosvg\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etermtosvg /path/to/savefile.svg\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo record only the execution of a particular command, use the flag \u003ccode\u003e-c\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etermtosvg -c neofetch\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI recommend that you resize the terminal window so that the frames generated match the desired width and height. You can instead use the \u003ccode\u003e-g\u003c/code\u003e flag to pass in a geometry. (\u0026ldquo;100x30\u0026rdquo; creates a screen with 100 colums and 30 rows)\u003c/p\u003e\n\u003cp\u003eLastly, if you don\u0026rsquo;t want an animation, you can pass in \u003ccode\u003e-s\u003c/code\u003e and the result will be a folder of SVG files representing each frame.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etermtosvg -s\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHere is an example of an animation I made with this tool:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/202201171031.svg\" alt=\"\"\u003e\u003c/p\u003e\n",
    "date_published": "2022.01.17",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "ad8c533610b5"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/latexlistlabels/",
    "url": "https://brandonrozek.com/blog/latexlistlabels/",
    "title": "LaTex List Labels",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eA quick tip that I recently learned is that the symbols in a LaTex list item is changeable. In fact, the following technique works for both the \u003ccode\u003eenumerate\u003c/code\u003e and \u003ccode\u003eitemize\u003c/code\u003e environments.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{itemize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[$\\square$]\u003c/span\u003e  Item 1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[$\\triangle$]\u003c/span\u003e Item 2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{itemize}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{enumerate}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[$\\rho_1$]\u003c/span\u003e Property 1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[$\\rho_2$]\u003c/span\u003e Property 2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e\\item\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e[$\\rho_3$]\u003c/span\u003e Property 3\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{enumerate}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/202201162357.svg\" alt=\"\"\u003e\u003c/p\u003e\n",
    "date_published": "2022.01.16",
    "tags": ["LaTex"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "b978cbe10da3"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/socks5proxy/",
    "url": "https://brandonrozek.com/blog/socks5proxy/",
    "title": "Configuring a SOCKS5 Proxy",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eA SOCKS5 proxy allows you to have network traffic as if it was coming from the proxy server as opposed to your local client. You can easily set it up using SSH from your local machine.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003essh -D \u003cspan style=\"color:#ae81ff\"\u003e1337\u003c/span\u003e -N user@remotehost\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe above command opens the port 1337 on localhost and redirects traffic from that port over the SSH connection to the remote machine.\u003c/p\u003e\n\u003cp\u003eOn the client computer, you can then go to your browser settings and specify \u003ccode\u003elocalhost:1337\u003c/code\u003e as a SOCKS5 proxy. Some commands allow you to specify a proxy in their flags. For example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl --proxy socks5h://localhost:1337 https://brandonrozek.com/\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNot all commands make this easy however, which is where \u003ccode\u003eproxychains\u003c/code\u003e comes in. It allows you to route traffic from a specified subcommand over a proxy. For example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eproxychains curl https://brandonrozek.com\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo do this you will need proxychains installed. On Ubuntu, the package is called \u003ccode\u003eproxychains4\u003c/code\u003e. Then you\u0026rsquo;ll need to edit the bottom of \u003ccode\u003e/etc/proxychains4.conf\u003c/code\u003e to contain the following line:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esocks5  127.0.0.1  1337\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2022.01.03",
    "tags": ["Networking","SSH"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "ed506a09a2b2"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/networkxtree/",
    "url": "https://brandonrozek.com/blog/networkxtree/",
    "title": "Tree-based Plots in NetworkX",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eA graph in D3 and NetworkX can be represented as a JSON file.\u003c/p\u003e\n\u003cp\u003eExample:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;nodes\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\u003cspan style=\"color:#f92672\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;A\u0026#34;\u003c/span\u003e},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\u003cspan style=\"color:#f92672\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e, \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;B\u0026#34;\u003c/span\u003e},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\u003cspan style=\"color:#f92672\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e, \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;C\u0026#34;\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;links\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\u003cspan style=\"color:#f92672\"\u003e\u0026#34;source\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\u003cspan style=\"color:#f92672\"\u003e\u0026#34;target\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        {\u003cspan style=\"color:#f92672\"\u003e\u0026#34;source\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\u003cspan style=\"color:#f92672\"\u003e\u0026#34;target\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003enodes\u003c/code\u003e entry in the JSON is a list containing a \u003ccode\u003enode\u003c/code\u003e object. Each \u003ccode\u003enode\u003c/code\u003e object has a unique id and a name which can appear inside the node in the drawing.\u003c/p\u003e\n\u003cp\u003eThe \u003ccode\u003elinks\u003c/code\u003e entry in the JSON is a list of \u003ccode\u003elink\u003c/code\u003e objects which each denote a (directed) edge between a source and target id.\u003c/p\u003e\n\u003cp\u003eTo run the following script you\u0026rsquo;ll need \u003ccode\u003egraphwiz\u003c/code\u003e installed on the system. You\u0026rsquo;ll also need to have the python packages \u003ccode\u003enetworkx\u003c/code\u003e and \u003ccode\u003epydot\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e networkx \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e nx\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e networkx.readwrite \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e json_graph\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e networkx.drawing.nx_pydot \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e graphviz_layout\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e matplotlib.pyplot \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e plt\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Replace graph_json variable with JSON representation of graph\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egraph_json \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e {\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;nodes\u0026#34;\u003c/span\u003e: [{\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;A\u0026#34;\u003c/span\u003e},{\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;B\u0026#34;\u003c/span\u003e}],\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;links\u0026#34;\u003c/span\u003e: [{\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;source\u0026#34;\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;target\u0026#34;\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e}]}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enode_labels \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e {node[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;id\u0026#39;\u003c/span\u003e]:node[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;name\u0026#39;\u003c/span\u003e] \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e node \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e graph_json[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;nodes\u0026#39;\u003c/span\u003e]}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e graph_json[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;nodes\u0026#39;\u003c/span\u003e]:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edel\u003c/span\u003e n[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;name\u0026#39;\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eg \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e json_graph\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003enode_link_graph(graph_json, directed\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eTrue\u003c/span\u003e, multigraph\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eFalse\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epos \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e graphviz_layout(g, prog\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;dot\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enx\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003edraw(g\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eto_directed(), pos, labels\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003enode_labels, with_labels\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eTrue\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eshow()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eGiven the JSON from the top of the post it\u0026rsquo;ll produce:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20220103000837137.png\" alt=\"image-20220103000837137\"\u003e\u003c/p\u003e\n",
    "date_published": "2022.01.02",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "c8cffb5c4078"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/networkdebugging/",
    "url": "https://brandonrozek.com/blog/networkdebugging/",
    "title": "Common Network Debugging Commands",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eBelow are list of commands that I use to debug common issues in a network. There is a wonderful tool called Wireshark which you can use to sniff packets in a network and filter by a wide range of options, but we\u0026rsquo;ll mainly focus on simple tools that you can use in the terminal.\u003c/p\u003e\n\u003ch2 id=\"ping\"\u003e\u003ccode\u003eping\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eThe most commonly used networking command is \u003ccode\u003eping\u003c/code\u003e. This allows you to see the time it takes to send and receive an ICMP packet from/to a specified address. Most people use Google\u0026rsquo;s DNS server \u003ccode\u003e8.8.8.8\u003c/code\u003e as a quick test to see if they have access to the Internet.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eping 8.8.8.8\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ePING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.\n64 bytes from 8.8.8.8: icmp_seq=1 ttl=117 time=267 ms\n64 bytes from 8.8.8.8: icmp_seq=2 ttl=117 time=74.9 ms\n64 bytes from 8.8.8.8: icmp_seq=3 ttl=117 time=34.7 ms\n64 bytes from 8.8.8.8: icmp_seq=4 ttl=117 time=298 ms\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003ePress CTRL-C when you are done looking at the output. Here is a list of common flags used in the ping command.\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eFlag\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-c NUM\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eOnly send/receive an ICMP packet \u003ccode\u003eNUM\u003c/code\u003e number of times\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-D\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003ePrint the timestamp along with the roundtrip time\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-W timeout\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eWaits a \u003ccode\u003etimeout\u003c/code\u003e amount of seconds for the response before moving on to the next ICMP roundtrip\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eExample:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eping -c3 -D -W1 1.1.1.1\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ePING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.\n[1641156381.342990] 64 bytes from 1.1.1.1: icmp_seq=1 ttl=57 time=52.6 ms\n[1641156382.555358] 64 bytes from 1.1.1.1: icmp_seq=2 ttl=57 time=263 ms\n[1641156383.327286] 64 bytes from 1.1.1.1: icmp_seq=3 ttl=57 time=34.3 ms\n\n--- 1.1.1.1 ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2003ms\nrtt min/avg/max/mdev = 34.275/116.670/263.116/103.823 ms\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"ip-route\"\u003e\u003ccode\u003eip route\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eWithout any extra flags or subcommands this will give you a view of your routing table. A routing table specifies for a given address range, which device to send the network traffic over. Normally you see a large routing table inside businesses (or if you access a lot of VPNs at once). Here is an example of a typical one in a household.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eip route\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edefault via 192.168.0.1 dev wlan0 proto dhcp metric \u003cspan style=\"color:#ae81ff\"\u003e600\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e192.168.0.0/24 dev wlan0 proto kernel scope link src 192.168.0.2 metric \u003cspan style=\"color:#ae81ff\"\u003e600\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis says that any address between 192.168.0.1-254 goes over the \u003ccode\u003ewlan0\u003c/code\u003e device which on some computers  denote WiFi. The first line shows what the default entry/gateway is, that is if the ip address you\u0026rsquo;re trying to access is not listed in the table it will go through the IP listed in that row first.\u003c/p\u003e\n\u003cp\u003eYou can manually add and remove entries in the routing table as well.\u003c/p\u003e\n\u003cp\u003eExample:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo ip route add 192.168.1.0/24 dev wlan0\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo ip route del 192.168.1.0/24 dev wlan0\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"traceroute\"\u003e\u003ccode\u003etraceroute\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eThis command is more useful if you have multiple segmented networks and you\u0026rsquo;re trying to figure out at which layer the connection failed. Recently I used this to debug some directional WiFi extenders.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etraceroute 8.8.8.8\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etraceroute to 8.8.8.8 \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e8.8.8.8\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e64\u003c/span\u003e hops max\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e   192.168.0.1  2.051ms  2.003ms  1.278ms \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e   192.168.2.1  5.743ms  5.647ms  3.592ms \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e   192.168.5.1  5.754ms  63.285ms  7.187ms \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e   72.126.142.1  96.056ms  101.861ms  14.547ms \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e   103.51.11.206  87.273ms  16.617ms  72.810ms \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#ae81ff\"\u003e6\u003c/span\u003e   170.222.220.217  13.745ms  101.122ms  16.402ms \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#ae81ff\"\u003e7\u003c/span\u003e   201.149.23.6  85.738ms  102.977ms  100.974ms \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#ae81ff\"\u003e8\u003c/span\u003e   107.150.228.33  15.111ms  87.467ms  103.076ms \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#ae81ff\"\u003e9\u003c/span\u003e   12.21.20.233  100.755ms  102.000ms  102.352ms \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e   8.8.8.8  102.505ms  102.085ms  101.762ms \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"dig\"\u003e\u003ccode\u003edig\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eWe\u0026rsquo;ve been talking about IP addresses with the last few commands, but there can be problems in the domain name resolution as well. A domain name is what you commonly type in the browser such as \u003ccode\u003educkduckgo.com\u003c/code\u003e. Your computer will then ask the DNS server it knows about what the IP of that address is.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edig duckduckgo.com\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e\n; \u0026lt;\u0026lt;\u0026gt;\u0026gt; DiG \u0026lt;\u0026lt;\u0026gt;\u0026gt; duckduckgo.com\n;; global options: +cmd\n;; Got answer:\n;; -\u0026gt;\u0026gt;HEADER\u0026lt;\u0026lt;- opcode: QUERY, status: NOERROR, id: 36469\n;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 65494\n;; QUESTION SECTION:\n;duckduckgo.com.                        IN      A\n\n;; ANSWER SECTION:\nduckduckgo.com.         149     IN      A       52.149.246.39\n\n;; Query time: 88 msec\n;; SERVER: 127.0.0.53#53(127.0.0.53)\n;; WHEN: Sun Jan 02 16:06:23 EST 2022\n;; MSG SIZE  rcvd: 59\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eMost linux systems have a DNS cache server setup which makes it difficult to figure out what the upstream DNS server that it\u0026rsquo;s querying is. Mainly because it can be configured a myriad of ways. If you are using NetworkManager you can use the following command:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enmcli dev show | grep DNS\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn some other cases it would be in \u003ccode\u003e/etc/resolv.conf\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecat /etc/resolv.conf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"arp\"\u003e\u003ccode\u003earp\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eLastly at the lowest level, arp will tell you the MAC addresses of IP addresses you have communicated with before.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003earp\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eAddress                  HWtype  HWaddress           Flags Mask            Iface\n192.168.0.1              ether   10:1d:b1:1d:1f:91   C                     wlan0\n192.168.0.11             ether   72:25:22:2c:72:72   C                     wlan0\n192.168.0.111            ether   03:33:34:3b:23:39   C                     wlan0\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2022.01.02",
    "tags": ["Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "7a55c1f5f40e"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/leantactics/",
    "url": "https://brandonrozek.com/blog/leantactics/",
    "title": "Lean 3 Theorem Prover Tactics",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWarning: This post covers Lean 3 which has been deprecated\nin favor of Lean 4.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve recently been playing with the Lean 3 Theorem Prover.  I am impressed with how some of the mathematics community decided to extend this project via \u003ca href=\"https://leanprover-community.github.io/\"\u003emathlib\u003c/a\u003e and really make proving theorems in this framework easy and enjoyable.\u003c/p\u003e\n\u003cp\u003eNormally one of the most frustrating parts of theorem proving is having to justify what may seem to be a simple goal. Luckily, mathlib helps us out by \u003ca href=\"https://leanprover-community.github.io/mathlib_docs/tactics.html\"\u003eintroducing tactics\u003c/a\u003e that can take some of these simple goals to the finish line.\u003c/p\u003e\n\u003ch2 id=\"tactics\"\u003eTactics\u003c/h2\u003e\n\u003cp\u003eHere is a subset of tactics that I find myself using the most:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ca href=\"#hint\"\u003ehint\u003c/a\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ca href=\"#cases_type*\"\u003ecases_type*\u003c/a\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ca href=\"#have\"\u003ehave\u003c/a\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ca href=\"#itauto\"\u003eitauto\u003c/a\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ca href=\"#pretty_cases\"\u003epretty_cases\u003c/a\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ca href=\"#squeeze_simp\"\u003esqueeze_simp\u003c/a\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ca href=\"#tidy\"\u003etidy\u003c/a\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ca href=\"#solve_by_elim\"\u003esolve_by_elim\u003c/a\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ca href=\"#finish\"\u003efinish\u003c/a\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ca href=\"#linarith\"\u003elinarith\u003c/a\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"hint\"\u003e\u003ccode\u003ehint\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eThe first command to reach for. This will give you a tactic that is guaranteed to succeed.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem running_example1: ∀ P Q : Prop,\n  Q ∧ (Q → P) → P\n:=\nbegin\n  intros P Q H,\n  hint,\n-- the following tactics solve the goal:\n-- Try this: tauto\n-- Try this: itauto\n-- Try this: finish\n-- Try this: solve_by_elim\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eDocumentation: \u003ca href=\"https://leanprover-community.github.io/mathlib_docs/tactics.html#hint\"\u003ehttps://leanprover-community.github.io/mathlib_docs/tactics.html#hint\u003c/a\u003e\u003c/p\u003e\n\u003ch3 id=\"cases_type\"\u003e\u003ccode\u003ecases_type*\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eThe following destructs all (con/dis)junctions in the current context\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003ecases_type* or and,\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eExample:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem running_example1: ∀ P Q : Prop,\n  Q ∧ (Q → P) → P\n:=\nbegin\n  intros P Q H,\n  cases_type* or and,\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe tactic state before:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eP Q: Prop\nH : Q ∧ (Q → P)\n⊢ P\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe tactic state after:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eP Q: Prop\nH_left: Q\nH_right: (Q → P)\n⊢ P\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eDocumentation: \u003ca href=\"https://leanprover-community.github.io/mathlib_docs/tactics.html#cases_type\"\u003ehttps://leanprover-community.github.io/mathlib_docs/tactics.html#cases_type\u003c/a\u003e\u003c/p\u003e\n\u003ch3 id=\"have\"\u003e\u003ccode\u003ehave\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eIf you want to add a hypothesis, this is a great way to introduce that and solve that goal. Combine this with \u003ccode\u003elibrary_search\u003c/code\u003e to try to find simple tactics to justify the hypothesis.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003ehave H : EXAMPLE := by library_search,\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eExample:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem running_example1: ∀ P Q : Prop,\n  Q ∧ (Q → P) → P\n:=\nbegin\n  intros P Q H,\n  cases_type* or and,\n  have H_P : P := by library_search,\n-- Try this: exact H_right H_left\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eDocumentation: \u003ca href=\"https://leanprover-community.github.io/mathlib_docs/tactics.html#have\"\u003ehttps://leanprover-community.github.io/mathlib_docs/tactics.html#have\u003c/a\u003e\u003c/p\u003e\n\u003ch3 id=\"itauto\"\u003e\u003ccode\u003eitauto\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eTries to use \u003ca href=\"https://plato.stanford.edu/entries/logic-intuitionistic/\"\u003eintuitionist\u003c/a\u003e propositional logic to solve the goal. Also called constructivist logic, it does not allow the use of the \u003ca href=\"https://en.wikipedia.org/wiki/Law_of_excluded_middle\"\u003elaw of excluded middle\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eExample:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem running_example1: ∀ P Q : Prop,\n  Q ∧ (Q → P) → P\n:=\nbegin\n  intros P Q H,\n  cases_type* or and,\n  itauto,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eDocumentation: \u003ca href=\"https://leanprover-community.github.io/mathlib_docs/tactics.html#itauto\"\u003ehttps://leanprover-community.github.io/mathlib_docs/tactics.html#itauto\u003c/a\u003e\u003c/p\u003e\n\u003ch3 id=\"pretty_cases\"\u003e\u003ccode\u003epretty_cases\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eGreat to call after \u003ccode\u003edestruct\u003c/code\u003e, \u003ccode\u003einduction\u003c/code\u003e, \u003ccode\u003einjection\u003c/code\u003e, etc. This will give you a cleaner way of representing cases.\u003c/p\u003e\n\u003cp\u003eExample:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem running_example2: ∀ n : ℕ,\n  (∃ m : ℕ, n = 2 * m) ∨ (∃ m : ℕ, n = 2 * m + 1)\n:=\nbegin\nintros,\ninduction n,\npretty_cases,\n-- Try this:\n  -- case nat.zero\n  -- { admit },\n  -- case nat.succ : n_n n_ih\n  -- { admit }\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eDocumentation: \u003ca href=\"https://leanprover-community.github.io/mathlib_docs/tactics.html#pretty_cases\"\u003ehttps://leanprover-community.github.io/mathlib_docs/tactics.html#pretty_cases\u003c/a\u003e\u003c/p\u003e\n\u003ch3 id=\"squeeze_simp\"\u003e\u003ccode\u003esqueeze_simp\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eThis tactic will give you the sequence of rewrite rules that can simplify an expression.\u003c/p\u003e\n\u003cp\u003eExample:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem running_example2: ∀ n : ℕ,\n  (∃ m : ℕ , n = 2 * m) ∨ (∃ m : ℕ, n = 2 * m + 1)\n  :=\nbegin\nintros,\ninduction n,\ncase nat.zero\n{\n  left,\n  existsi 0,\n  squeeze_simp,\n  -- Try this: simp only [mul_zero]\n },\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eDocumentation: \u003ca href=\"https://leanprover-community.github.io/mathlib_docs/tactics.html#squeeze_simp%20/%20squeeze_simpa%20/%20squeeze_dsimp%20/%20squeeze_scope\"\u003ehttps://leanprover-community.github.io/mathlib_docs/tactics.html#squeeze_simp%20/%20squeeze_simpa%20/%20squeeze_dsimp%20/%20squeeze_scope\u003c/a\u003e\u003c/p\u003e\n\u003ch3 id=\"tidy\"\u003e\u003ccode\u003etidy\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eApplies simple tactics to try to solve the goal. I like to use the query variant \u003ccode\u003etidy?\u003c/code\u003e in order to get a list of tactics back.\u003c/p\u003e\n\u003cp\u003eThe tactics it uses are: \u003ccode\u003eid_rhs\u003c/code\u003e, \u003ccode\u003erefl\u003c/code\u003e, \u003ccode\u003eexact\u003c/code\u003e, \u003ccode\u003etrivial\u003c/code\u003e, \u003ccode\u003edismp\u003c/code\u003e, \u003ccode\u003esimp\u003c/code\u003e, \u003ccode\u003einjections_and_clear\u003c/code\u003e, \u003ccode\u003esolve_by_elim\u003c/code\u003e, \u003ccode\u003enorm_cast\u003c/code\u003e, \u003ccode\u003eunfold\u003c/code\u003e, \u003ccode\u003efsplit\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eExample:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem running_example3: ∀ n : ℕ,\n  (∃ m : ℕ , n = 2 * m) ∨ (∃ m : ℕ, n = 2 * m + 1)\n  :=\nbegin\nintros,\ninduction n,\ncase nat.zero\n{\n  left,\n  existsi 0,\n  simp only [mul_zero],\n },\ncase nat.succ : n IH\n{\n  cases_type* or,\n  case or.inl : IH\n  {\n    right,\n    cases IH with m IH,\n    tidy?,\n    -- Try this: fsplit, work_on_goal 1 { solve_by_elim }\n  },\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eDocumentation: \u003ca href=\"https://leanprover-community.github.io/mathlib_docs/tactics.html#tidy\"\u003ehttps://leanprover-community.github.io/mathlib_docs/tactics.html#tidy\u003c/a\u003e\u003c/p\u003e\n\u003ch3 id=\"solve_by_elim\"\u003e\u003ccode\u003esolve_by_elim\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eThis tactic tries to apply the current hypotheses in the context and apply congruence closure algorithms to solve your goal.\u003c/p\u003e\n\u003cp\u003eIf you have multiple goals you can call \u003ccode\u003esolve_by_elim*\u003c/code\u003e to try to solve them all at once. I find though that it is often more successful to do \u003ccode\u003eall_goals{solve_by_elim}\u003c/code\u003e instead, unless you need results from one of the subproofs in the other.\u003c/p\u003e\n\u003cp\u003eThis tactic is non-deterministic so it bounds the number of attempts to discharge subgoals. Here is how you increase the bound: \u003ccode\u003esolve_by_elim { max_depth := 5 }\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eExample:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem running_example3: ∀ n : ℕ,\n  (∃ m : ℕ , n = 2 * m) ∨ (∃ m : ℕ, n = 2 * m + 1)\n  :=\nbegin\nintros,\ninduction n,\ncase nat.zero\n{\n  left,\n  existsi 0,\n  simp only [mul_zero],\n },\ncase nat.succ : n IH\n{\n  cases_type* or,\n  case or.inl : IH\n  {\n    right,\n    cases IH with m IH,\n    fsplit, work_on_goal 1 { solve_by_elim },\n  },\n  case or.inr : IH\n  {\n    left,\n    cases IH with m IH,\n    rewrite IH,\n    existsi m + 1,\n    solve_by_elim,\n  },\n}\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eDocumentation: \u003ca href=\"https://leanprover-community.github.io/mathlib_docs/tactics.html#solve_by_elim\"\u003ehttps://leanprover-community.github.io/mathlib_docs/tactics.html#solve_by_elim\u003c/a\u003e\u003c/p\u003e\n\u003ch3 id=\"finish\"\u003e\u003ccode\u003efinish\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eThis tactic is like a black box to me, mainly because it performs many subtactics. It tries to finish off the goal. To give a hint of its power, it can actually solve the first theorem.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem running_example1: ∀ P Q: Prop,\n  Q ∧ (Q → P) → P :=\nbegin\n  finish,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eSource: \u003ca href=\"https://leanprover-community.github.io/mathlib_docs/tactics.html#finish%20/%20clarify%20/%20safe\"\u003ehttps://leanprover-community.github.io/mathlib_docs/tactics.html#finish%20/%20clarify%20/%20safe\u003c/a\u003e\u003c/p\u003e\n\u003ch3 id=\"linarith\"\u003e\u003ccode\u003elinarith\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eThis tactic solves linear (in)equalities.\u003c/p\u003e\n\u003cp\u003eExample:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-lean\" data-lang=\"lean\"\u003etheorem example3: ∀ n m : ℕ,\n  n \u0026gt; m → m \u0026lt; n :=\nbegin\n  intros,\n  linarith,\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eDocumentation: \u003ca href=\"https://leanprover-community.github.io/mathlib_docs/tactics.html#linarith\"\u003ehttps://leanprover-community.github.io/mathlib_docs/tactics.html#linarith\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eA common pattern of writing proofs for me is to use a combination of \u003ccode\u003ehint\u003c/code\u003e and \u003ccode\u003ehave\u003c/code\u003e with \u003ccode\u003elibrary_search\u003c/code\u003e. Especially when you are not an expert in a theorem prover, it\u0026rsquo;s nice to have the system fill in some of the simpler steps. I generally prefer tactics that give you a list of simpler tactics back as opposed to solving the goal in the background without any proof. Regardless, I\u0026rsquo;m glad that many of these decision procedures exist to help me deal with what can sometimes be the verbosity of theorem proving.\u003c/p\u003e\n",
    "date_published": "2021.10.11",
    "tags": ["Formal Methods"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "adea1a69ddc8"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/convert-djvu-to-pdf/",
    "url": "https://brandonrozek.com/blog/convert-djvu-to-pdf/",
    "title": "Convert DJVU to PDF",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve recently come across the DJVU file format before and needed to convert it to a PDF. The most reliable way I\u0026rsquo;ve found to do it is via the following command.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edjvups FILENAME | ps2pdf - OUTPUT_FILE\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhere FILENAME first gets converted to the PS file format which then gets converted to a PDF with the name OUTPUT_FILE. To make things easier, I wrote a little script that does this process automatically while preserving the filename.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o errexit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o nounset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o pipefail\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eshow_usage\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Usage: djvu2pdf [FILENAME]\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$#\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -ne \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    show_usage\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e ! command -v djvups \u0026gt; /dev/null ; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;djvups not found. Exiting...\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e ! command -v ps2pdf \u0026gt; /dev/null ; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ps2pdf not found. Exiting...\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edjvups \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | ps2pdf - \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003e1%.*\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e.pdf\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2021.08.28",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "ddf23ae64f9a"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/do-while-other-lang/",
    "url": "https://brandonrozek.com/blog/do-while-other-lang/",
    "title": "Do-While Loop in Other Languages",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eSome languages like C, C++, and Java have a concept of a Do-While loop which normally look like the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edo {\n    statements;\n} while(condition);\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThis would ensure that your group of statements at least run once and then continue while the condition is still met. If you\u0026rsquo;re used to that pattern, then it can be annoying when you switch to another language like Python and find that it doesn\u0026rsquo;t exist. To replicate this behavior, its as simple as adding an extra variable.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efirst_run \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eTrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e condition \u003cspan style=\"color:#f92672\"\u003eor\u003c/span\u003e first_run:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    first_run \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eFalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    statements\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2021.08.28",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "5a9c792673f2"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/antenna-basics/",
    "url": "https://brandonrozek.com/blog/antenna-basics/",
    "title": "Antenna Basics",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen a radio wave hits an antenna, the electrical component of the wave induces a difference of potential in the conductor which gives rise to an electric current. This is called electromotive force. The current induced is normally small, therefore, we usually design antennas to be as efficient as possible.\u003c/p\u003e\n\u003cp\u003eWays to increase efficiency:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eEnsure that the impedance of the antenna matches what it\u0026rsquo;s hooked up to.\u003c/li\u003e\n\u003cli\u003eCap the ends of the antenna with an insulator\u003c/li\u003e\n\u003cli\u003eIf there is a power line nearby, run the antenna at a right angle to that wire to decrease interference.\u003c/li\u003e\n\u003cli\u003eMake the antenna resonant at the frequency you\u0026rsquo;re interested in. You can do this by adjusting the length of the wire, inductance, capacitance.\u003c/li\u003e\n\u003cli\u003eDesign the antenna so that it focuses the energy from the direction you care about, as opposed to listening equally in all directions.\u003c/li\u003e\n\u003cli\u003eSimilarly, raise the antenna higher off the ground so that more of it\u0026rsquo;s beam pattern reaches the horizon.\u003c/li\u003e\n\u003c/ul\u003e\n",
    "date_published": "2021.07.25",
    "tags": ["Amateur Radio"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "fdec15d43776"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/parallel-scp/",
    "url": "https://brandonrozek.com/blog/parallel-scp/",
    "title": "Parallel SCP with LFTP",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eSegmented file transfer allows you to split up a file into multiple chunks and download them in parallel. There is a program written for Linux called LFTP which can accomplish this task and supports FTP, HTTP, SFTP, BitTorrent, among others. The syntax is a little funky, so I wrote a wrapper I call \u003ccode\u003epget\u003c/code\u003e which allows for segmented file transfers using SCP.\u003c/p\u003e\n\u003cp\u003eUsage:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epget \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003euser@host\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003efile\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003esegments?\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003ewhere \u003ccode\u003esegments?\u003c/code\u003e is optional.\u003c/p\u003e\n\u003cp\u003eExample:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epget user@127.0.0.1 Documents/todo.txt \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe full script:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/sh\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# set -xv\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eshow_usage\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Usage: pget [user@host] [file] [segments?]\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Example: pget user@127.0.0.1 Documents/todo.txt 3\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Currently only supports sftp. Segments value are optional.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econtains_help_flag\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;-h\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;--help\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$#\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -lt \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$#\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -gt \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   contains_help_flag \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   contains_help_flag \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$2\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   contains_help_flag \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$3\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e ; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    show_usage\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eREMOTE_HOST\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eFILE_LOCATION\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$2\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$#\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -eq \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    NUM_SEGMENTS\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$3\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    NUM_SEGMENTS\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;1\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLFTP_COMMAND\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;pget -n \u003c/span\u003e$NUM_SEGMENTS\u003cspan style=\"color:#e6db74\"\u003e \u003c/span\u003e$FILE_LOCATION\u003cspan style=\"color:#e6db74\"\u003e;bye\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elftp -e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$LFTP_COMMAND\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e sftp://\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$REMOTE_HOST\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2021.07.25",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "610d337c8783"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/socatforward/",
    "url": "https://brandonrozek.com/blog/socatforward/",
    "title": "Forward Packets with Socat",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve written about relaying TCP traffic using \u003ca href=\"/blog/sshlocalportforwarding/\"\u003eSSH port forwarding\u003c/a\u003e. Though sometimes you don\u0026rsquo;t require the authenticity and encryption of SSH or want to use another protocol such as UDP. That\u0026rsquo;s where \u003ccode\u003esocat\u003c/code\u003e comes in.\u003c/p\u003e\n\u003cp\u003eThe following will listen to TCP traffic on port 8001 and redirect it to TCP localhost:8000\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esocat tcp-listen:8001,reuseaddr,fork tcp:localhost:8000\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will listen UDP on port 4009 and forward it to UDP localhost:4010\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esocat udp-listen:4009,reuseaddr,fork udp:localhost:4010\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2021.06.18",
    "tags": ["Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "790558856bee"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/terminaloutputvim/",
    "url": "https://brandonrozek.com/blog/terminaloutputvim/",
    "title": "Terminal Output in Vim",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn Vim you can output the result of a command below your cursor by using \u003ccode\u003e:r!\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eExamples:\u003c/p\u003e\n\u003cp\u003eHello World\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e:r! echo Hello World\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe current timestamp\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e:r! echo \u0026#34;[$(date \u0026#39;+\\%Y-\\%m-\\%d \\%H:\\%M:\\%S\u0026#39;)]\u0026#34;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eOutputs: \u003ccode\u003e[2021-06-18 16:13:19]\u003c/code\u003e\u003c/p\u003e\n",
    "date_published": "2021.06.18",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "fba948955d22"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/netbootxyz/",
    "url": "https://brandonrozek.com/blog/netbootxyz/",
    "title": "Netboot.xyz Bootloader",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eInstead of manually loading ISOs onto a USB stick for \u003ca href=\"/blog/ventoy/\"\u003eVentoy\u003c/a\u003e to display, we can use Netboot.xyz to present us a list of options and download them during boot. This requires an internet connection in order to work.\u003c/p\u003e\n\u003cp\u003eNetboot.xyz is commonly used for PXE booting, but in this post I\u0026rsquo;ll describe using it as an ISO.\u003c/p\u003e\n\u003cp\u003eDownload the \u003ca href=\"https://boot.netboot.xyz/ipxe/netboot.xyz.iso\"\u003eNetboot ISO\u003c/a\u003e and \u003ca href=\"/blog/ddforiso/\"\u003eload it onto a flash drive\u003c/a\u003e. Then boot a computer from the flash drive and you should see something similar to this animation from their website:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/netboot.xyz.gif\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eAnother benefit of this approach over Ventoy is that we don\u0026rsquo;t have to manually update the flash drive. It always comes fresh with the ISOs available on their website.\u003c/p\u003e\n",
    "date_published": "2021.06.18",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "d99cafd95a34"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/ventoy/",
    "url": "https://brandonrozek.com/blog/ventoy/",
    "title": "Ventoy for managing ISOs on one USB",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eTraditionally I would hold one Linux distribution per USB drive. Though with 32 GB USB Flash drives only costing $12, why hold only one per drive? That\u0026rsquo;s where Ventoy comes in. With Ventoy, we can store multiple ISOs which it then presents as a boot screen.\u003c/p\u003e\n\u003cp\u003eTo install Ventoy onto a flash drive, follow the \u003ca href=\"https://www.ventoy.net/en/doc_start.html\"\u003eGet Started\u003c/a\u003e guide on their website. Then once it\u0026rsquo;s installed you should see a mountpoint labeled \u0026ldquo;Ventoy\u0026rdquo;. There you can drop off any ISOs you want to appear on the boot screen. Here are some examples of what I keep in mine:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eClonezilla\u003c/li\u003e\n\u003cli\u003eKali Linux\u003c/li\u003e\n\u003cli\u003eKubuntu\u003c/li\u003e\n\u003cli\u003eTails\u003c/li\u003e\n\u003cli\u003eUBCD\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/ventoy.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eExample screenshot from their website.\u003c/p\u003e\n",
    "date_published": "2021.06.18",
    "tags": ["Virtualization"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "b096fed9be3c"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/comparatorlogicgate/",
    "url": "https://brandonrozek.com/blog/comparatorlogicgate/",
    "title": "Comparator Logic Gate",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis post is heavily derived from the Wikipedia post on \u003ca href=\"https://en.wikipedia.org/wiki/Digital_comparator\"\u003eDigital Comparators\u003c/a\u003e and therefore can be distributed under the Creative Commons Attribution-ShareAlike 3.0 license.\u003c/p\u003e\n\u003cp\u003eTo compare two binary numbers, we need to find a way to check if two single bits are equal or if one is greater than the other.\u003c/p\u003e\n\u003ch2 id=\"checking-single-bit-equality\"\u003eChecking Single Bit Equality\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s construct the truth table:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eA\u003c/th\u003e\n          \u003cth\u003eB\u003c/th\u003e\n          \u003cth\u003e$A = B$\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e0\u003c/td\u003e\n          \u003ctd\u003e0\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n          \u003ctd\u003e0\u003c/td\u003e\n          \u003ctd\u003e0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e0\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n          \u003ctd\u003e0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eThe only two ways this evaluates to true is if\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eA is 1 and B is 1\u003c/li\u003e\n\u003cli\u003eA is 0 and B is 0\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eTherefore,\n$$\n(A = B) \\equiv AB + \\bar{A}\\bar{B} \\equiv \\overline{(A \\oplus B)}\n$$\u003c/p\u003e\n\u003ch2 id=\"single-bit-comparator\"\u003eSingle Bit Comparator\u003c/h2\u003e\n\u003cp\u003eTo check if one bit is greater than the other, let us first look at the truth table:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eA\u003c/th\u003e\n          \u003cth\u003eB\u003c/th\u003e\n          \u003cth\u003e$\\bar{B}$\u003c/th\u003e\n          \u003cth\u003e$A \u0026gt; B$\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e0\u003c/td\u003e\n          \u003ctd\u003e0\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n          \u003ctd\u003e0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n          \u003ctd\u003e0\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e0\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n          \u003ctd\u003e0\u003c/td\u003e\n          \u003ctd\u003e0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n          \u003ctd\u003e0\u003c/td\u003e\n          \u003ctd\u003e0\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eThe only way for this to evaluate to true is if A is 1 and B is 0. That is $(A \u0026gt; B) \\equiv A\\bar{B}$\u003c/p\u003e\n\u003ch2 id=\"l-bit-comparator\"\u003eL-Bit Comparator\u003c/h2\u003e\n\u003cp\u003eTo compare an entire bitstring, we start from the most significant bit and check to see if one bit is greater than the other. If not, it will then check the next bit while confirming that all the previous bits were the same. For a 3-bit comparator, the logic will look like the following:\n$$\n(A_3A_2A_1 \u0026gt; B_3 B_2 B_1) \\equiv A_3\\bar{B_3} + \\overline{(A_3 \\oplus B_3)}A_2\\bar{B_2} + \\overline{(A_3 \\oplus B_3)}\\overline{(A_2 \\oplus B_2)}A_1\\bar{B_1}\n$$\u003c/p\u003e\n",
    "date_published": "2021.06.18",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "7bbc125ead40"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/z3constraintsolving/",
    "url": "https://brandonrozek.com/blog/z3constraintsolving/",
    "title": "Z3 Constraint solving",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve been looking for an easy to use constraint solver for a while and recently I\u0026rsquo;ve landed on using the python bindings for the SMT solver Z3.\u003c/p\u003e\n\u003cp\u003eTo install:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install z3-solver\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLet\u0026rsquo;s say you want to find non-negative solutions for the following Diophantine equation:\n$$\n9x - 100y = 1\n$$\nTo do that, we import Z3, declare our integer variables, and pass it into a solve function:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e z3 \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ex, y \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Ints(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;x y\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esolve(\u003cspan style=\"color:#ae81ff\"\u003e9\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e x \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e y \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, x \u003cspan style=\"color:#f92672\"\u003e\u0026gt;=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, y \u003cspan style=\"color:#f92672\"\u003e\u0026gt;=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will print out: \u003ccode\u003e[y = 8, x = 89]\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eIf you want to use these values for later computations, you\u0026rsquo;ll have to setup a Z3 model:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e z3 \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ex, y \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Ints(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;x y\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003es \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Solver\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003es\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd(\u003cspan style=\"color:#ae81ff\"\u003e9\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e x \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e y \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003es\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd(x \u003cspan style=\"color:#f92672\"\u003e\u0026gt;=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003es\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd(y \u003cspan style=\"color:#f92672\"\u003e\u0026gt;=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003es\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003echeck()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003em \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e s\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emodel()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ex_val \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e m\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eeval(x)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ey_val \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e m\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eeval(y)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2021.06.18",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "9e4ced42d47"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/printdebug/",
    "url": "https://brandonrozek.com/blog/printdebug/",
    "title": "Print Statements with Frame Information",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI find it extremely useful to include frame information such as filename, line number, and current function in my print statements. Here\u0026rsquo;s a couple ways that I\u0026rsquo;ve done that in the past.\u003c/p\u003e\n\u003cp\u003eIn C:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;stdio.h\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#define DEBUGLOG(msg) (printf(\u0026#34;[%s:%d %s] %s\\n\u0026#34;, __FILE__, __LINE__, __PRETTY_FUNCTION__, msg))\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eDEBUGLOG\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn Python:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e inspect \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e currentframe, getframeinfo\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edebuglog\u003c/span\u003e(m):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    frame \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e currentframe()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    frame_info \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e getframeinfo(frame)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    last_frame \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e frame\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ef_back\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    last_frame_info \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e getframeinfo(last_frame)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    filename \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e last_frame_info\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efilename \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e last_frame_info \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026lt;stdin\u0026gt;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    lineno \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e last_frame_info\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elineno \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e last_frame_info \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    function_name \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e last_frame_info\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efunction \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e last_frame_info \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026lt;module\u0026gt;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003ef\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;[\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{\u003c/span\u003efilename\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e:\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{\u003c/span\u003elineno\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{\u003c/span\u003efunction_name\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e] \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{\u003c/span\u003em\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2021.06.03",
    "tags": ["Testing"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "a811c52bda55"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/introrfpoweramp/",
    "url": "https://brandonrozek.com/blog/introrfpoweramp/",
    "title": "Introduction to RF Power Amplifiers",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eFor field day I\u0026rsquo;ve been toying with the idea of buying a power amplifier for my HackRF. What I\u0026rsquo;ve come to realize is that there are a lot more to power amplifiers than just how much it amplifies by. This post outlines my current understanding (I\u0026rsquo;m by no means an expert) on the subject of RF power amplifiers.\u003c/p\u003e\n\u003cp\u003eI opened up mini-circuits and was greeted with the following table. (Shorted for sake of demonstration)\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eModel Number\u003c/th\u003e\n          \u003cth\u003eF Low (MHz)\u003c/th\u003e\n          \u003cth\u003eF High (MHz)\u003c/th\u003e\n          \u003cth\u003eGain (dB)\u003c/th\u003e\n          \u003cth\u003eNF (dB)\u003c/th\u003e\n          \u003cth\u003eP1dB\u003c/th\u003e\n          \u003cth\u003eOIP3 (dBm)\u003c/th\u003e\n          \u003cth\u003eInput VSWR\u003c/th\u003e\n          \u003cth\u003eOutput VSWR\u003c/th\u003e\n          \u003cth\u003eVoltage (V)\u003c/th\u003e\n          \u003cth\u003eCurrent (mA)\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eLZV-22+\u003c/td\u003e\n          \u003ctd\u003e0.1\u003c/td\u003e\n          \u003ctd\u003e200\u003c/td\u003e\n          \u003ctd\u003e43\u003c/td\u003e\n          \u003ctd\u003e8.9\u003c/td\u003e\n          \u003ctd\u003e42\u003c/td\u003e\n          \u003ctd\u003e52\u003c/td\u003e\n          \u003ctd\u003e1.4\u003c/td\u003e\n          \u003ctd\u003e4\u003c/td\u003e\n          \u003ctd\u003e24\u003c/td\u003e\n          \u003ctd\u003e6000\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eZHL-1W-63-S+\u003c/td\u003e\n          \u003ctd\u003e600\u003c/td\u003e\n          \u003ctd\u003e6000\u003c/td\u003e\n          \u003ctd\u003e35\u003c/td\u003e\n          \u003ctd\u003e12\u003c/td\u003e\n          \u003ctd\u003e30\u003c/td\u003e\n          \u003ctd\u003e35\u003c/td\u003e\n          \u003ctd\u003e2.5\u003c/td\u003e\n          \u003ctd\u003e3.5\u003c/td\u003e\n          \u003ctd\u003e15\u003c/td\u003e\n          \u003ctd\u003e1000\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"definitions\"\u003eDefinitions\u003c/h3\u003e\n\u003cp\u003eF Low: Lowest frequency supported by the power amplifier\u003c/p\u003e\n\u003cp\u003eF High: Highest frequency supported by the power amplifier\u003c/p\u003e\n\u003cp\u003eGain (dB): The difference between the output power and  the input power\u003c/p\u003e\n\u003cp\u003eNoise Figure (NF): The amount of additional noise that gets added to the system by using the power amplifier\u003c/p\u003e\n\u003cp\u003eP1dB:  This is the point in where the output power is 1dB less than the linear projected power. This is expressed as the value of the output power. This is commonly used to describe the point in which the power amplifier turns non-linear or approaches saturation.\u003c/p\u003e\n\u003cp\u003eSaturation: The point in where adding more input power does not increase the output power.\u003c/p\u003e\n\u003cp\u003eThird order intercept point (OIP3): Currently unclear as to what this is.\u003c/p\u003e\n\u003cp\u003eVoltage Standing Wave Radio (VSWR): It\u0026rsquo;s a function of return loss and describes how well impedance is matched. We want this to be as close to 1 as possible. More information: \u003ca href=\"https://antenna-theory.com/m/definitions/vswr.php\"\u003ehttps://antenna-theory.com/m/definitions/vswr.php\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eFrom P1dB you can derive a notion of maximum input power. Take your P1dB point and subtract out the gain.\n$$\nInput_{max} = P1dB - Gain\n$$\nFor the LZV-22+, the maximum input power before P1dB is reached is -1dBm. This measure is separate than a \u0026ldquo;Maximum Input Power\u0026rdquo; given by a datasheet. Those are usually the maximum power allowed before the amplifier gets damaged.\u003c/p\u003e\n\u003cp\u003eTo calculate the power required to drive an amplifier, multiply the value listed in voltage by the current. From this you can then derive the efficiency of a power amplifier at any given input power. For example, let us look at LZV-22+ at it\u0026rsquo;s P1dB point.\u003c/p\u003e\n\u003cp\u003ePower required to drive = 24V * 6000mA / 1000mA = 144W\u003c/p\u003e\n\u003cp\u003eOutput power at P1dB = 42dBm = ~16W\u003c/p\u003e\n\u003cp\u003eEfficiency = 16 / 144 * 100 = ~11%\u003c/p\u003e\n\u003cp\u003eWith that background, let\u0026rsquo;s think about my field day setup. For a technician level\u0026rsquo;s amateur radio license, you likely only need an amplifier that supports 28MHz-1GHz. The HackRF outputs 1mW at most frequencies. As a starting point, it would be good to get that to 1W. Most specifications are listed in logithmic scale.\n$$\nP_{dBm} = 10\\log_{10}{(P_W)} + 30\n$$\nHence, 1mW is 0dBm and 1W is 30dBm.\u003c/p\u003e\n\u003cp\u003eConsiderations:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eNeed a gain of 30dB\u003c/li\u003e\n\u003cli\u003eLower noise figure is always better but a link budget analysis would get you a specific number.\u003c/li\u003e\n\u003cli\u003eWant VSWR as close to 1 as possible.\u003c/li\u003e\n\u003c/ul\u003e\n",
    "date_published": "2021.04.10",
    "tags": ["Amateur Radio"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "a8510ebe7754"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/human-readable-sizes/",
    "url": "https://brandonrozek.com/blog/human-readable-sizes/",
    "title": "Human Readable Sizes",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen playing with large and small values, it is useful to convert them to a different unit in scientific notation. Let\u0026rsquo;s look at bytes.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esize_categories \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;B\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;KB\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;MB\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;GB\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;TB\u0026#34;\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can figure out how to best represent it by seeing how many of the base (in this case 1000) fits into the value.\n$$\ncategory = \\lfloor \\frac{\\log{(size_{bytes})}}{\\log{(base)}} \\rfloor\n$$\nYou\u0026rsquo;ll want to make sure that you don\u0026rsquo;t overflow in the number of categories you have\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecategory_num \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e min(category_num, len(size_categories))\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can then get its category representation by\n$$\nsize = \\frac{size_{bytes}}{(2^{category})}\n$$\nWe can wrap this all up info a nice python function\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehumanReadableBytes\u003c/span\u003e(num_bytes: int) \u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003e str:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    base \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Zero Case\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e num_bytes \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;0\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    size_categories \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;B\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;KB\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;MB\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;GB\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;TB\u0026#34;\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    category_num \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e int(math\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elog(num_bytes) \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e math\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elog(base))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Make sure it doesn\u0026#39;t overflow\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    category_num \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e min(category_num, len(size_categories) \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{:.2f}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e \u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eformat(num_bytes \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e (base \u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003e category_num)) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \\\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\tsize_categories[category_num]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2021.03.15",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "2a32b08bd2c1"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/detectpythonversion/",
    "url": "https://brandonrozek.com/blog/detectpythonversion/",
    "title": "Detect Python Version",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI was working on a distribution recently where \u003ccode\u003epython\u003c/code\u003e was mapped to \u003ccode\u003epython2\u003c/code\u003e. It mixed me up for a bit since I was writing a script for \u003ccode\u003epython3\u003c/code\u003e but it ran partially under \u003ccode\u003epython2\u003c/code\u003e. To lower confusion in the future, I think it\u0026rsquo;s a great idea to check the python version and exit if it isn\u0026rsquo;t the version you expect.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e sys \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e version_info, exit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e version_info\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emajor \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;This script only supports Python 3\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Curent version: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \\\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          str(version_info\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emajor) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;.\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \\\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          str(version_info\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eminor) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;.\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \\\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          str(version_info\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emicro)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Exiting...\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2021.03.15",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "f742be560b7f"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/stow/",
    "url": "https://brandonrozek.com/blog/stow/",
    "title": "Stow Version Manager",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eSuppose you need a newer or specific version of a package that isn\u0026rsquo;t included in your standard linux repo. The standard procedure is to do something like the following\u0026hellip;\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit clone https://github.com/brandon-rozek/project.git\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd project\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e./configure\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emake\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo make install\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDepending on how the package is configured, it will put the executable, configurations, and libraries in \u003ccode\u003e/usr/local/\u003c/code\u003e or other places in your system.\u003c/p\u003e\n\u003cp\u003eThe difficulty comes, when it is time to change versions of the software. Some people include a nice \u003ccode\u003emake uninstall\u003c/code\u003e target which can help you track down which files were installed. Other software packages aren\u0026rsquo;t so convinient. This is where stow comes in. It allows you to easily change between software versions.\u003c/p\u003e\n\u003cp\u003eHow does this work? First let\u0026rsquo;s create a stow directory in \u003ccode\u003e/usr/local\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mkdir /usr/local/stow\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen let\u0026rsquo;s go to the project we want to install and add a flag to configure to tell it where to install\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit clone https://github.com/brandon-rozek/project.git\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd project\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e./configure --prefix\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/usr/local/stow/project-v1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emake\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo make install\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor cmake based projects\u0026hellip;\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit clone https://github.com/brandon-rozek/cmake-project.git\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd cmake-project\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir build\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd build\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecmake3 -DCMAKE_INSTALL_PREFIX\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/usr/local/stow/cmake-project-v1  ..\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emake\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo make install\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow let\u0026rsquo;s check out the installation directroy\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd /usr/local/stow/project-v1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003els\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eetc\nlib\nbin\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eYou\u0026rsquo;ll notice here that we\u0026rsquo;ll have the same setup as \u003ccode\u003e/usr/local/etc/\u003c/code\u003e, \u003ccode\u003e/usr/local/lib/\u003c/code\u003e and \u003ccode\u003e/usr/local/bin\u003c/code\u003e. That\u0026rsquo;s because when we run \u003ccode\u003estow\u003c/code\u003e it\u0026rsquo;ll go through and create the appropriate symlinks to the files.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd /usr/local/stow\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo stow project-v1\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"stow-flags\"\u003eStow Flags\u003c/h2\u003e\n\u003cp\u003eStow will go into the folder and then create the appropriate symlinks relative to the parent directory of stow, which in this case is \u003ccode\u003e/usr/local\u003c/code\u003e. If you don\u0026rsquo;t want it to create the symlinks in the parent directory then you can set the target somewhere else with \u003ccode\u003e--target\u003c/code\u003e or \u003ccode\u003e-t\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd /home/brandon/stow\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo stow --target /usr/local project-v1\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you don\u0026rsquo;t want to \u003ccode\u003ecd\u003c/code\u003e into your stow directory, you can set \u003ccode\u003e--dir\u003c/code\u003e or \u003ccode\u003e-d\u003c/code\u003e to indicate where that is.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo stow --dir /home/brandon/stow --target /usr/local project-v1\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSummary of Flags (From the Man page)\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eFlag\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--no\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eDo not perform any operations that modify the filesystem; merely show what would happen.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--dir=DIR\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eSet the stow directory to \u0026ldquo;DIR\u0026rdquo; instead of the current directory.  This  also has the effect of making the default target directory be the parent of \u0026ldquo;DIR\u0026rdquo;.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--target=DIR\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eSet the target directory to \u0026ldquo;DIR\u0026rdquo; instead of the parent of the stow  directory.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--stow\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eStow the packages that follow this option into the target directory. This is the default action and so can be omitted if you are only stowing packages rather than performing a mixture of stow/delete/restow actions.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--delete\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eUnstow the packages that follow this option from the target directory  rather than installing them.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--restow\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eRestow packages (first unstow, then stow again).\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--no-folding\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eDisable folding of newly stowed directories when stowing, and refolding  of newly foldable directories when unstowing.\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"applications\"\u003eApplications\u003c/h2\u003e\n\u003ch3 id=\"switching-between-versions\"\u003eSwitching between versions\u003c/h3\u003e\n\u003cp\u003eThis makes changing versions of software really easy.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd /usr/local/stow\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo stow -D project-v1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo stow project-v2\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd like that you\u0026rsquo;ve switched versions from 1 to 2!\u003c/p\u003e\n\u003ch3 id=\"managing-dot-files\"\u003eManaging dot files\u003c/h3\u003e\n\u003cp\u003eThis is also a great way to store dot files for your user directory. Here is an example of what I do:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/home/brandon/stow\n├── bash\n│   └── .bashrc\n├── git\n│   └── .gitconfig\n└── tmux\n    └── .tmux.conf\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"troubleshooting\"\u003eTroubleshooting\u003c/h2\u003e\n\u003cp\u003eIf you get a command not found for an executable you know lives in \u003ccode\u003e/usr/local/bin\u003c/code\u003e then it might be because your \u003ccode\u003ePATH\u003c/code\u003e isn\u0026rsquo;t setup to look there.\u003c/p\u003e\n\u003cp\u003eInside your \u003ccode\u003e~/.bash_rc\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport PATH\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$PATH:/usr/local/bin\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2021.02.25",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "14639c9db6a4"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/clearingsystemdlogs/",
    "url": "https://brandonrozek.com/blog/clearingsystemdlogs/",
    "title": "Clearing Systemd Logs",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eShort post today. I wanted to clear out some disk space usage on one of my servers and noticed that the systemd logs were taking up a decent bit. Here are two options to clear out old logs.\u003c/p\u003e\n\u003cp\u003eHere is an example to do it by time, let\u0026rsquo;s say 15 days:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ejournalctl --vacuum-time\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e15d\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAn example to clear it by total disk usage, let\u0026rsquo;s say 10Gb:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ejournalctl --vacuum-size\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e10G\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2021.02.21",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "acf4ee0e7f0c"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/librecalctips/",
    "url": "https://brandonrozek.com/blog/librecalctips/",
    "title": "LibreOffice Calc Tips",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve been working with LibreOffice Calc (or Excel) spreadsheets recently and wanted to share some of the things I\u0026rsquo;ve learned.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAbsolute vs Relative Cell References\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThe main difference between referencing a cell relatively vs absolute is that the absolute reference includes a \u003ccode\u003e$\u003c/code\u003e in the front. This is important if you want to drag a formula across multiple cells.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-excel\" data-lang=\"excel\"\u003e$A:$B\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003eReferring to a sheet name\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eTo refer to cells in another sheet, first begin the reference with the sheet name, then a period, follow by the cells you wish to reference in that sheet. If the sheet name has spaces in it, then you need to wrap it in quotes.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-excel\" data-lang=\"excel\"\u003e\u0026#39;Another Sheet\u0026#39;.A:B\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003eReferring to a column\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eTo refer to a single column, you need to repeat the column name separated by a colon.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-excel\" data-lang=\"excel\"\u003eB:B\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003eGet row that matches a query\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eFor this we\u0026rsquo;ll use the \u003ccode\u003eMATCH\u003c/code\u003e function. It takes three parameters:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eThe value to match\u003c/li\u003e\n\u003cli\u003eThe range of cells to query over\u003c/li\u003e\n\u003cli\u003eWhich comparison function to use. Use \u003ccode\u003e0\u003c/code\u003e for equality.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eIt will then return the first row number that matches the query.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-excel\" data-lang=\"excel\"\u003eMATCH(\u0026#34;Bob\u0026#34;, B:B, 0)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003eQuery a value based on another from that row.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eTo do this, we will need to combine both the \u003ccode\u003eINDEX\u003c/code\u003e function and the \u003ccode\u003eMATCH\u003c/code\u003e function. The \u003ccode\u003eINDEX\u003c/code\u003e function takes three parameters:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eThe range of cells to reference\u003c/li\u003e\n\u003cli\u003eThe row number\u003c/li\u003e\n\u003cli\u003eThe column number\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eUse the \u003ccode\u003eMATCH\u003c/code\u003e function as the second argument, and you can reference another column of a row based on a query.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-excel\" data-lang=\"excel\"\u003eINDEX(A:B, MATCH(\u0026#34;Bob\u0026#34;, B:B, 0), 1)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003eRefer to a value in a nearby cell\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eWith the \u003ccode\u003eOFFSET\u003c/code\u003e function you can refer to a cell relative to another. Its parameters are:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eReference Cell\u003c/li\u003e\n\u003cli\u003eRow Offset\u003c/li\u003e\n\u003cli\u003eColumn Offset\u003c/li\u003e\n\u003cli\u003eHeight\u003c/li\u003e\n\u003cli\u003eWidth\u003c/li\u003e\n\u003c/ol\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-excel\" data-lang=\"excel\"\u003e# To see the value in the row above A5 (A4)\nOFFSET(A5, -1, 0, 1, 1)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003eConcatenate Strings\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eStrings separated by \u003ccode\u003e\u0026amp;\u003c/code\u003e are concatenated together.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-excel\" data-lang=\"excel\"\u003e\u0026#34;Hello \u0026#34; \u0026amp; \u0026#34;World.\u0026#34;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003eMirror a cell from a different sheet\u003c/strong\u003e\u003csup id=\"fnref:1\"\u003e\u003ca href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\"\u003e1\u003c/a\u003e\u003c/sup\u003e\u003c/p\u003e\n\u003cp\u003eSuppose there\u0026rsquo;s a value in a different sheet at the same position that we want. For example, a common header bar, a summary table, etc.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-excel\" data-lang=\"excel\"\u003e=LET(val, INDIRECT(\u0026#34;Sheet1.\u0026#34; \u0026amp; ADDRESS(ROW(),COLUMN())), IF(val=\u0026#34;\u0026#34;, \u0026#34;\u0026#34;, val))\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003ePlace this in \u003ccode\u003eA1\u003c/code\u003e and we\u0026rsquo;ll get the value from \u003ccode\u003eSheet1:A1\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThe \u003ccode\u003eADDRESS\u003c/code\u003e command takes two required parameters: the row value and column value. From this, it produces a string representing the cell location.\u003c/p\u003e\n\u003cp\u003eThe \u003ccode\u003eINDIRECT\u003c/code\u003e command creates a \u003cem\u003ereference\u003c/em\u003e based on the cell location represented as a string.\u003c/p\u003e\n\u003cp\u003eThe \u003ccode\u003eLET\u003c/code\u003e command takes three parameters:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThe variable name\u003c/li\u003e\n\u003cli\u003eThe value that the variable name evaluates to\u003c/li\u003e\n\u003cli\u003eThe expression which uses the variable name\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThe \u003ccode\u003eIF\u003c/code\u003e command is used so that if the referenced cell is empty then it does not show a zero.\u003c/p\u003e\n\u003cdiv class=\"footnotes\" role=\"doc-endnotes\"\u003e\n\u003chr\u003e\n\u003col\u003e\n\u003cli id=\"fn:1\"\u003e\n\u003cp\u003eThanks to hwn for this suggestion!\u0026#160;\u003ca href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\"\u003e\u0026#x21a9;\u0026#xfe0e;\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/div\u003e\n",
    "date_published": "2021.02.20",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "c55df201e882"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pidhcp/",
    "url": "https://brandonrozek.com/blog/pidhcp/",
    "title": "DHCP for Raspberry Pi",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eRecently I ran across the use case where I needed a Raspberry Pi to be connected to the Internet via a WiFI connection, while also providing DHCP leases through an Ethernet connection. I couldn\u0026rsquo;t find a great way to achieve this with \u003ccode\u003edhcpcd\u003c/code\u003e so instead I grabbed a tool that I\u0026rsquo;m more familiar with \u003ccode\u003ednsmasq\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eBefore we begin, we need to setup a static IP for the Ethernet interface (\u003ccode\u003eeth0\u003c/code\u003e). Since the Ethernet interface will not be connected to the Internet, there\u0026rsquo;s no need to worry about a lot of the usual fields.\u003c/p\u003e\n\u003cp\u003eIn \u003ccode\u003e/etc/dhcpcd.conf\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003einterface eth0\nfallback nodhcp\n\nprofile nodhcp\nstatic ip_address=192.168.2.1/24\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eBelow is the config for \u003ccode\u003ednsmasq\u003c/code\u003e which lives in \u003ccode\u003e/etc/dnsmasq.conf\u003c/code\u003e. It essentially tells it to only serve DHCP leases on the Ethernet connection of the Raspberry Pi. The addresses it\u0026rsquo;ll lease out according to the config below is between 192.168.0.50-192.168.0.150 and is leased for 12 hours.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e# Configuration file for dnsmasq.\n#\n# Format is one option per line, legal options are the same\n# as the long options legal on the command line. See\n# \u0026#34;/usr/sbin/dnsmasq --help\u0026#34; or \u0026#34;man 8 dnsmasq\u0026#34; for details.\n\n# Listen on this specific port instead of the standard DNS port\n# (53). Setting this to zero completely disables DNS function,\n# leaving only DHCP and/or TFTP.\nport=0\n\n# If you want dnsmasq to listen for DHCP and DNS requests only on\n# specified interfaces (and the loopback) give the name of the\n# interface (eg eth0) here.\n# Repeat the line for more than one interface.\ninterface=eth0\n# Or you can specify which interface _not_ to listen on\n#except-interface=\n# Or which to listen on by address (remember to include 127.0.0.1 if\n# you use this.)\n#listen-address=\n\n# Uncomment this to enable the integrated DHCP server, you need\n# to supply the range of addresses available for lease and optionally\n# a lease time. If you have more than one network, you will need to\n# repeat this for each network on which you want to supply DHCP\n# service.\ndhcp-range=192.168.0.50,192.168.0.150,12h\n\n# Enable DHCPv6. Note that the prefix-length does not need to be specified\n# and defaults to 64 if missing/\n#dhcp-range=1234::2, 1234::500, 64, 12h\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2021.02.16",
    "tags": ["Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "8c13511f5b45"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/bashvalidateip/",
    "url": "https://brandonrozek.com/blog/bashvalidateip/",
    "title": "Quick Bash: Validate IP Address",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ccode\u003eipcalc\u003c/code\u003e is a terminal tool that lets you validate an IP address. This proves useful to me as I have scripts that automate certain remote tasks given an IP address. Instead of trusting that an argument passed is a valid IP, why not check it?\u003c/p\u003e\n\u003cp\u003eFirst the script would need to check if \u003ccode\u003eipcalc\u003c/code\u003e exists.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e ! command -v ipcalc \u0026gt; /dev/null ; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ipcalc not found. Exiting...\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow for this example, we\u0026rsquo;ll validate an IP address stored in the variable \u003ccode\u003e$IP\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e ! ipcalc -cs \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$IP\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e ; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Invalid IP Address\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.12.20",
    "tags": ["Bash","Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "c7a65890d9d9"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/quickbashargcount/",
    "url": "https://brandonrozek.com/blog/quickbashargcount/",
    "title": "Quick Bash: Check Argument Count",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve been writing more bash scripts recently and I noticed that I often check for the number of arguments before validating them in my scripts. I\u0026rsquo;ll share that small snippet here for my future self.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/sh\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o errexit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o nounset\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset -o pipefail\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eshow_usage\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Usage: script [arg1]\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check argument count\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$#\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -ne \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    show_usage\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.12.15",
    "tags": ["Bash"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "ac38c9d9d896"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/mirroringwithgitea/",
    "url": "https://brandonrozek.com/blog/mirroringwithgitea/",
    "title": "Mirroring with Gitea",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eSites like Github, Gitlab, and BitBucket have nice UIs that make looking at commit diffs and issue tracking easier. However, this requires an internet connection. What if we can mirror the content from these sites locally? Gitea comes to the rescue!\u003c/p\u003e\n\u003ch2 id=\"installation\"\u003eInstallation\u003c/h2\u003e\n\u003cp\u003eDownload gitea and make it executable.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewget -O gitea https://dl.gitea.io/gitea/1.13.0/gitea-1.13.0-linux-amd64\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echmod +x gitea\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eCreate the stow directory\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mkdir /usr/local/stow/gitea-1.13.0\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eStart by moving the executable to this directory\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mkdir /usr/local/stow/gitea-1.13.0/bin\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mv gitea-1.13.0-linux-amd64 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e        /usr/local/gitea-1.13.0/bin/\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAdd a \u003ccode\u003egit\u003c/code\u003e user that gitea will use.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo adduser \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e   --system \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e   --shell /bin/bash \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e   --gecos \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Git Version Control\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e   --group \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e   --disabled-password \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e   --home /home/git \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e   git\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eCreate directories gitea expects.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mkdir -p /usr/local/stow/gitea-1.13.0/var/lib/gitea/\u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003ecustom,data,log\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo chown -R git:git /usr/local/stow/gitea-1.13.0/var/lib/gitea\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo chmod -R \u003cspan style=\"color:#ae81ff\"\u003e750\u003c/span\u003e /usr/local/stow/gitea-1.13.0/var/lib/gitea\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mkdir -p /usr/local/stow/gitea-1.13.0/etc/gitea\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo chown root:git /usr/local/stow/gitea-1.13.0/etc/gitea\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo chmod \u003cspan style=\"color:#ae81ff\"\u003e770\u003c/span\u003e /usr/local/stow/gitea-1.13.0/etc/gitea\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eStow gitea\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd /usr/local/stow\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo stow gitea-1.13.0\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"systemd-service\"\u003eSystemd Service\u003c/h2\u003e\n\u003cp\u003eEdit \u003ccode\u003e/usr/lib/systemd/system/gitea.service\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eGitea (Git with a cup of tea)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAfter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003esyslog.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAfter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enetwork.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRequires\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enginx.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003esimple\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eUser\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003egit\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eGroup\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003egit\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWorkingDirectory\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/local/var/lib/gitea\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/local/bin/gitea web --config /usr/local/etc/gitea/app.ini\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eEnvironment\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eUSER=git HOME=/home/git GITEA_WORK_DIR=/usr/local/var/lib/gitea\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eStart and enable gitea.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl start gitea\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl enable gitea\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"gitea-setup\"\u003eGitea Setup\u003c/h2\u003e\n\u003cp\u003eGo to http://localhost:3000/install\u003c/p\u003e\n\u003cp\u003eCheck \u0026ldquo;Enable Local Mode\u0026rdquo;\nFor security, check \u0026ldquo;Disable Self-Registration\u0026rdquo; and \u0026ldquo;Require Sign-In to View Pages\u0026rdquo;\nSetup the \u0026ldquo;Administrator Account Settings\u0026rdquo;\u003c/p\u003e\n\u003ch2 id=\"nginx-setup\"\u003eNginx Setup\u003c/h2\u003e\n\u003cp\u003eEdit \u003ccode\u003e/etc/nginx/sites-available/gitea.conf\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003elocation\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/gitea/\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eproxy_pass\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ehttp://localhost:3000/\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eproxy_http_version\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e.1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eX-Real-IP\u003c/span\u003e  $remote_addr;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eX-Forwarded-For\u003c/span\u003e $remote_addr; \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eHost\u003c/span\u003e $host;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eEnable the site\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo ln -s \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e        /etc/nginx/sites-available/gitea.conf \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e        /etc/nginx/sites-enabled/\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl restart nginx\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"apache-httpd-setup\"\u003eApache HTTPD Setup\u003c/h2\u003e\n\u003cp\u003eEdit \u003ccode\u003e/etc/httpd/conf.d/gitea.conf\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e\u0026lt;Location /gitea\u0026gt;\n    ProxyPass http://localhost:3000 retry=0 timeout=5\n    ProxyPassReverse http://localhost:3000\n    ProxyPreserveHost On\n\n    Order allow,deny\n    Allow from all\n\u0026lt;/Location\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eRestart httpd\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl restart httpd\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"mirroring\"\u003eMirroring\u003c/h2\u003e\n\u003cp\u003eTo mirror a repo, first click on the plus sign and select \u0026ldquo;New Migration\u0026rdquo;, then make sure to check the \u0026ldquo;This repository will be a mirror\u0026rdquo;.\u003c/p\u003e\n",
    "date_published": "2020.12.08",
    "tags": ["Archive"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "ce7a39da156b"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/mirrorhugosite/",
    "url": "https://brandonrozek.com/blog/mirrorhugosite/",
    "title": "Mirror a Hugo Site",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;m a big proponent of having offline copies of content. Especially when I\u0026rsquo;m on travel and don\u0026rsquo;t have easy Internet access. Using the built in hugo webserver and a reverse proxy, I will show how we can host a local mirror of a Hugo site.\u003c/p\u003e\n\u003ch2 id=\"systemd-service\"\u003eSystemd Service\u003c/h2\u003e\n\u003cp\u003eTo begin with, let\u0026rsquo;s create a systemd service that will start up the Hugo webserver automatically. To demonstrate this, I will show a mirror of my website.\u003c/p\u003e\n\u003cp\u003eEdit \u003ccode\u003e/usr/lib/systemd/system/brandonrozek.service\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eMirror of brandonrozek.com\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWants\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ehttpd.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAfter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ehttpd.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eUser\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ebrandon\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003esimple\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWorkingDirectory\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/home/brandon/repo/brandonrozek.com\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/hugo serve --appendPort=false -D -p 22160 -b http://localhost/brandonrozek\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReplace \u003ccode\u003ehttpd\u003c/code\u003e with whatever reverse proxy service this depends on. For every site you mirror, you need to make sure that the port (specified by \u003ccode\u003e-p\u003c/code\u003e) is different.\u003c/p\u003e\n\u003cp\u003eNow let\u0026rsquo;s start and enable the service.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl start brandonrozek\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl enable brandonrozek\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"nginx-config\"\u003eNginx Config\u003c/h2\u003e\n\u003cp\u003eIf you are using Nginx as your reverse proxy, we will need to create a config at \u003ccode\u003e/etc/nginx/sites-available/brandonrozek.conf\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eserver\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elocation\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/brandonrozek\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_pass\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ehttp://localhost:22160\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_http_version\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e.1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eX-Real-IP\u003c/span\u003e  $remote_addr;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eX-Forwarded-For\u003c/span\u003e $remote_addr; \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eHost\u003c/span\u003e $host;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen to enable it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo ln -s \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e        /etc/nginx/sites-available/brandonrozek.conf \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e        /etc/nginx/sites-enabled/\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl restart httpd\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"apache-httpd-config\"\u003eApache HTTPD Config\u003c/h2\u003e\n\u003cp\u003eIf you are using apache as your reverse proxy, create a config at \u003ccode\u003e/etc/httpd/conf.d/brandonrozek.conf\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e\u0026lt;Location /brandonrozek\u0026gt;\n    ProxyPass http://localhost:22160/brandonrozek retry=0 timeout=5\n    ProxyPassReverse http://localhost:22160/brandonrozek\n    ProxyPreserveHost On\n\n    Order allow,deny\n    Allow from all\n\u0026lt;/Location\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThen restart the \u003ccode\u003ehttpd\u003c/code\u003e service.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl restart httpd\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.12.08",
    "tags": ["Archive","Hugo"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "75a8b76182aa"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/autostartdesktopapps/",
    "url": "https://brandonrozek.com/blog/autostartdesktopapps/",
    "title": "Autostart Desktop Applications",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe \u003ca href=\"https://web.archive.org/web/20240105053937/https://specifications.freedesktop.org/autostart-spec/0.5/ar01s02.html\"\u003efreedesktop specification\u003c/a\u003e describes how to identify file types, launch applications, and other useful desktop functions. A useful spec I\u0026rsquo;ve found recently is for launching desktop applications when you log into your machine.\u003c/p\u003e\n\u003cp\u003eIn my Kubuntu 20.10 system, the directory \u003ccode\u003e$HOME/.config/autostart\u003c/code\u003e can contain \u003ca href=\"/blog/linuxdesktopicons/\"\u003e\u003ccode\u003e.desktop\u003c/code\u003e\u003c/a\u003e files that describes the applications to start on login. Drop whichever desktop file you wish to start there. In other systems it may be located under \u003ccode\u003e$XDG_CONFIG_DIRS/autostart\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eIf you want to start up a script instead, you can put the script under \u003ccode\u003e$HOME/.config/autostart-scripts\u003c/code\u003e.\u003c/p\u003e\n",
    "date_published": "2020.11.29",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "f765346971a5"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/chrootvirtfilesystem/",
    "url": "https://brandonrozek.com/blog/chrootvirtfilesystem/",
    "title": "Chroot and Virtual Filesystems",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen running applications under a \u003ca href=\"https://en.wikipedia.org/wiki/Chroot\"\u003e\u003ccode\u003echroot\u003c/code\u003e\u003c/a\u003e environment, it can be annoying when certain \u003ca href=\"https://opensource.com/article/19/3/virtual-filesystems-linux\"\u003evirtual filesystems\u003c/a\u003e are unavailable. Here are the commands to mount the most common ones:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mount -t proc /proc /mnt/root/proc\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mount -o bind /sys /mnt/root/sys\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mount -o bind /dev /mnt/root/dev\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSource: \u003ca href=\"https://wiki.archlinux.org/index.php/chroot\"\u003eArchWiki\u003c/a\u003e\u003c/p\u003e\n",
    "date_published": "2020.11.29",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "37f5b4c46ae8"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pulseaudiortp/",
    "url": "https://brandonrozek.com/blog/pulseaudiortp/",
    "title": "Streaming PulseAudio over RTP",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWith PulseAudio, we can send a RTP stream over multicast UDP. Here is the bash commands to setup a sink where anything sent to it gets broadcasted to the multicast address 224.0.0.56 at port 46416.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epacmd load-module module-null-sink sink_name\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eRTP\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epacmd update-sink-proplist RTP device.description\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eRTP\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epacmd load-module module-rtp-send \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  source\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eRTP.monitor \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  destination_ip\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e224.0.0.56 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  port\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e46416\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen in \u003ccode\u003epavucontrol\u003c/code\u003e, you can push the audio of any application to the RTP sink we just created.\u003c/p\u003e\n",
    "date_published": "2020.11.24",
    "tags": ["Audio-Video"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "334b857013b9"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/multicastreceivescript/",
    "url": "https://brandonrozek.com/blog/multicastreceivescript/",
    "title": "Multicast Receive Script",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI use \u003ccode\u003esocat\u003c/code\u003e to debug mutlicast traffic, though the syntax for it is complicated to learn. Here is the command that I normally use to debug multicast traffic.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esocat UDP4-RECVFROM:\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$port\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e,ip-add-membership\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$multicast_address\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e:0.0.0.0,fork -\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis says to:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eListen to UDP traffic from \u003ccode\u003e$port\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eSubscribe to \u003ccode\u003e$multicast_address\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e0.0.0.0\u003c/code\u003e means to do it from the interface that matches the routing table for the multicast address.\u003c/li\u003e\n\u003cli\u003eThe rest makes it print the traffic to standard out.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eTo make life easier I created a little script called \u003ccode\u003emrecv\u003c/code\u003e that takes a multicast address and port and forms the socat command for me.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eshow_usage\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Usage: mrecv [multicast_address] [port]\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    exit \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econtains_help_flag\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;-h\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;--help\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$#\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -ne \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   contains_help_flag \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   contains_help_flag \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$2\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    show_usage\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emulticast_address\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eport\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$2\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esocat UDP4-RECVFROM:\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$port\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e,ip-add-membership\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$multicast_address\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e:0.0.0.0,fork -\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.11.18",
    "tags": ["Linux","Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": "6f6efe549a47"
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/customudev/",
    "url": "https://brandonrozek.com/blog/customudev/",
    "title": "Custom Device Paths with UDEV rules",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI wanted to create a rule so that when I plug in my rtlsdr, a symlink to the device will appear in \u003ccode\u003e/dev/rtlsdr\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eTo do this, first we need to get some uniquely identifying information about the SDR.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e❯ lsusb\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eBus \u003cspan style=\"color:#ae81ff\"\u003e001\u003c/span\u003e Device 002: ID 8087:8001 Intel Corp. Integrated Hub\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eBus \u003cspan style=\"color:#ae81ff\"\u003e001\u003c/span\u003e Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eBus \u003cspan style=\"color:#ae81ff\"\u003e003\u003c/span\u003e Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eBus \u003cspan style=\"color:#ae81ff\"\u003e002\u003c/span\u003e Device 017: ID 0bda:2838 Realtek Semiconductor Corp. RTL2838 DVB-T\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eBus \u003cspan style=\"color:#ae81ff\"\u003e002\u003c/span\u003e Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe part we care about here is the ID field. It is structured as \u003ccode\u003eID vendor:product\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eNow we can create a UDEV rule in \u003ccode\u003e/etc/udev/rules.d/\u003c/code\u003e that says \u0026ldquo;when we see this specific USB plugged in, create a symlink called \u003ccode\u003ertlsdr\u003c/code\u003e\u0026rdquo;\u003c/p\u003e\n\u003cp\u003e(/etc/udev/rules.d/rtlsdr.rules)\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eSUBSYSTEM==\u0026#34;usb\u0026#34;, ATTR{idVendor}==\u0026#34;0bda\u0026#34;, ATTR{idProduct}==\u0026#34;2838\u0026#34;, SYMLINK+=\u0026#34;rtlsdr\u0026#34;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTell udev to reload its rules\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo udevadm control --reload\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow once you have your device plugged in, you should be able to see it under \u003ccode\u003e/dev/rtlsdr\u003c/code\u003e or whatever you specified your symlink as.\u003c/p\u003e\n",
    "date_published": "2020.11.10",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/phonegpsforward/",
    "url": "https://brandonrozek.com/blog/phonegpsforward/",
    "title": "Forwarding Phone's GPS",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eSometimes I wish to open up \u003ca href=\"https://marble.kde.org/\"\u003eMarble\u003c/a\u003e which an a mapping application and see my current location on the globe. My laptop, however, doesn\u0026rsquo;t have a GPS module in it. Though I realized that I carry around a device that does\u0026hellip; My phone.\u003c/p\u003e\n\u003cp\u003eTiago Shibata wrote a great Android application called \u003ca href=\"https://www.f-droid.org/en/packages/io.github.tiagoshibata.gpsdclient/\"\u003eGPSd Forwarder\u003c/a\u003e which allows you to forward the NMEA data from your phone\u0026rsquo;s GPS to another device over UDP. The user interface of the app is really simple and only requires you to put an ip address and a port of the device you want to stream the GPS data to.\u003c/p\u003e\n\u003cp\u003eFor sake of example, let\u0026rsquo;s say we\u0026rsquo;re sending UDP packets to the ip address of 192.168.0.86 and a port of 29998.\u003c/p\u003e\n\u003cp\u003eAs a good sanity check, let\u0026rsquo;s make sure that the computer is receiving those UDP packets.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enc -lu \u003cspan style=\"color:#ae81ff\"\u003e29998\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou should see a constant stream of output that looks similar to:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$GNGSA,A,2,324,,,,,,,,,,,,1.2,0.9,0.8,3*05\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThen we need a piece of software that knows how to process this NMEA message. For that we use \u003ccode\u003egpsd\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eTo install,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install gpsd gpsd-clients\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor the purposes of this demo, we\u0026rsquo;ll disable the automatic socket daemon and tell gpsd to listen on the udp port 29998.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl stop gpsd.socket\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo gpsd -N udp://*:29998\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo sanity check that gpsd is processing the data, you can make sure there\u0026rsquo;s output in \u003ccode\u003egpsmon\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFinally to hook this into Marble, go to Settings -\u0026gt; Panel -\u0026gt; Location. Then in the location pane change the Position Tracking option to \u0026ldquo;gpsd\u0026rdquo;.\u003c/p\u003e\n",
    "date_published": "2020.11.08",
    "tags": ["GPS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/fmdigitalmodes/",
    "url": "https://brandonrozek.com/blog/fmdigitalmodes/",
    "title": "Getting Started with FM Digital Modes",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn this post, I will describe a low cost way to get started with digital modes using FM. We will extend off my \u003ca href=\"/blog/digitalmodes/\"\u003eprevious post\u003c/a\u003e. Even though some of these instructions are hardware specific, I hope that the general principles will apply to whatever hardware you\u0026rsquo;re working with.\u003c/p\u003e\n\u003cp\u003eIn order to transmit, you will need an amateur radio license. We are going to be transmitting within 145.50-145.80 Mhz which according to the \u003ca href=\"http://www.arrl.org/band-plan\"\u003eARRL Band Plan\u003c/a\u003e is marked as the miscellaneous and experimental section. In terms of our encoding scheme, let\u0026rsquo;s play around with \u003ca href=\"https://www.sigidwiki.com/wiki/8PSK\"\u003e8PSK\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"equipment\"\u003eEquipment\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eItem\u003c/th\u003e\n          \u003cth\u003ePurpose\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ca href=\"https://www.amazon.com/BaoFeng-BF-F8HP-Two-Way-136-174Mhz-400-520Mhz/dp/B00MAULSOK/\"\u003eBaoFeng BF-F8HP\u003c/a\u003e\u003c/td\u003e\n          \u003ctd\u003eTransmitter\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ca href=\"https://www.amazon.com/BTECH-APRS-K1-Interface-APRSDroid-Compatible/dp/B01LMIBAZW/\"\u003eAudio Interface Cable\u003c/a\u003e\u003c/td\u003e\n          \u003ctd\u003eCable that plugs from the transmitter to the computer\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ca href=\"https://www.amazon.com/LZYCO-External-Adapter-Headphone-Microphone/dp/B072J7WLQ5/\"\u003eUSB Audio Adapter\u003c/a\u003e (Optional)\u003c/td\u003e\n          \u003ctd\u003eIf your computer doesn\u0026rsquo;t have a 3.5mm audio jack\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eTransmitter Computer\u003c/td\u003e\n          \u003ctd\u003eModulates with Fldigi\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ca href=\"https://www.amazon.com/RTL-SDR-Blog-RTL2832U-Software-Defined/dp/B011HVUEME/\"\u003eSoftware Defined Radio\u003c/a\u003e\u003c/td\u003e\n          \u003ctd\u003eReceiver\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eReceiver Computer\u003c/td\u003e\n          \u003ctd\u003eDemodulates with Gqrx and decodes with Fldigi\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"transmitter-end\"\u003eTransmitter End\u003c/h2\u003e\n\u003cp\u003ePlug in the audio interface cable from the Baofeng to the transmitter computer. Then open up Fldigi and configure it to directly access the radio from the audio port. Refer to my previous post for some of the setup instructions.\u003c/p\u003e\n\u003cp\u003eFor our computer to handle transmitting, we need to disable push to talk on our Baofeng. To do that, we need to set VOX in the menu to 10.\u003c/p\u003e\n\u003cp\u003eIn Fldigi, go to Op mode and set it to 8PSK-125.\u003c/p\u003e\n\u003ch2 id=\"receiver-end\"\u003eReceiver End\u003c/h2\u003e\n\u003cp\u003eNow let\u0026rsquo;s plug in our software defined radio into our receiver computer and open up \u003ccode\u003egqrx\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eWhen you first open the application, you need to configure the IO devices. Here are the settings I use:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eParameter\u003c/th\u003e\n          \u003cth\u003eValue\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eDevice\u003c/td\u003e\n          \u003ctd\u003eRealtek RTL2838UHID\u0026hellip;\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eDevice String\u003c/td\u003e\n          \u003ctd\u003ertl=0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eInput Rate\u003c/td\u003e\n          \u003ctd\u003e2500000\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eBandwidth\u003c/td\u003e\n          \u003ctd\u003e0 MHz (Auto)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eLNA LO\u003c/td\u003e\n          \u003ctd\u003e0 MHz (Auto)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eDevice\u003c/td\u003e\n          \u003ctd\u003eDefault\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eSample Rate\u003c/td\u003e\n          \u003ctd\u003e48 KHz\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eAfter hitting \u003ccode\u003eOk\u003c/code\u003e, look for the play button on the top left of the application to start DSP.\u003c/p\u003e\n\u003cp\u003eSince we\u0026rsquo;re working in the 2 m band, we can set the LNA to 0 dB in the Input Controls. In the receiver options set the mode to Narrow FM and click R by the squelch to disable the squelch.\u003c/p\u003e\n\u003cp\u003eNext, we need to pipe the audio output to FLDIGI. For convenience, here is part of the instructions I had in the previous blog post.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epacmd load-module module-null-sink sink_name\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eMyLoopback\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epacmd update-sink-proplist MyLoopback device.description\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eMyLoopback\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epacmd load-module module-loopback sink\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eMyLoopback\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow open up \u003ccode\u003epavucontrol\u003c/code\u003e. On the Playback tab, change the output device of GQRX to be “MyLoopback”. On the Recording tab, change the input of fldigi to “Monitor of Null Output”.\u003c/p\u003e\n\u003cp\u003eIn Fldigi, we will need to set the Op Mode to 8PSK-125 to match the transmitter end.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eFinally, we can test our setup by typing a message in the blue box in FLDIGI on the transmitter machine. Hit the blue Tx button on the bottom right of the screen, and hope to see the message decoded on the receiver computer\u0026rsquo;s FLDIGI application.\u003c/p\u003e\n\u003cp\u003eOne of the biggest troubleshooting pieces for me is the fact that my laptop wants to mute new audio devices automatically. Make sure that MyLoopback is not muted.\u003c/p\u003e\n\u003cp\u003eAlso if you are having trouble with 8PSK, CW is easier to work with.\u003c/p\u003e\n",
    "date_published": "2020.11.07",
    "tags": ["Amateur Radio"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/personalsitepreservingurls/",
    "url": "https://brandonrozek.com/blog/personalsitepreservingurls/",
    "title": "Preserving Classic URLs on my Website",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eAt some point in 2018 I switched my website from using Wordpress on the backend to Hugo. I used a simple script to migrate everything over and in that migration a few things broke.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eCode snippets do not show up\u003c/li\u003e\n\u003cli\u003eImages don\u0026rsquo;t show up\u003c/li\u003e\n\u003cli\u003eURLs are different\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eI\u0026rsquo;ve gone back through some of my old posts and fixed up the code snippets. I usually decide to go back when I see sufficient traffic to one of those older pages.\u003c/p\u003e\n\u003cp\u003eI recently discovered that Hugo supported \u003ca href=\"https://gohugo.io/content-management/urls/#aliases\"\u003ealiases\u003c/a\u003e. This will allow me to setup redirect at the old URLs that my blog posts used to be in. I can do this by adding a small snippet to my front YAML.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003ealiases\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003e/2018/06/05/old/url/here\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will then redirect \u003ccode\u003ehttps://brandonrozek.com/2018/06/05/old/url/here\u003c/code\u003e to the current URL of the post I\u0026rsquo;m editing.\u003c/p\u003e\n\u003cp\u003eWhen I discovered this, I went ahead and redirected all my old URL blog posts to their current day versions. It makes me happy that I\u0026rsquo;m lowering the amount of link rot that I\u0026rsquo;m contributing to the web.\u003c/p\u003e\n\u003cp\u003eHow does it do this? It creates a simple webpage that contains a HTTP-Equiv tag which tells the browser to redirect it. Directly from the Hugo documentation:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u0026lt;!DOCTYPE html\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003ehtml\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;\u003cspan style=\"color:#f92672\"\u003ehead\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003etitle\u003c/span\u003e\u0026gt;https://example.com/posts/my-intended-url\u0026lt;/\u003cspan style=\"color:#f92672\"\u003etitle\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003elink\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erel\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;canonical\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehref\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://example.com/posts/my-intended-url\u0026#34;\u003c/span\u003e/\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003emeta\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;robots\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003econtent\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;noindex\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003emeta\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehttp-equiv\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;content-type\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003econtent\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;text/html; charset=utf-8\u0026#34;\u003c/span\u003e/\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003emeta\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehttp-equiv\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;refresh\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003econtent\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;0; url=https://example.com/posts/my-intended-url\u0026#34;\u003c/span\u003e/\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u0026lt;/\u003cspan style=\"color:#f92672\"\u003ehead\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ehtml\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.10.28",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pythonswig/",
    "url": "https://brandonrozek.com/blog/pythonswig/",
    "title": "C++ within Python with SWIG",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eFor performance reasons, it can be useful to write functions in C/C++ which can then be called within Python. This will be an introductory post, in where we will call a simple C++ function (with a dependency) within Python using \u003ca href=\"http://swig.org/\"\u003eSWIG\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eFirst we need to install SWIG:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install swig\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe\u0026rsquo;re going to use \u003ca href=\"https://gmplib.org/\"\u003eGNU MP\u003c/a\u003e in order to have arbitrary precision arithmetic for our factorial function.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install libgmp-dev\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"source-setup\"\u003eSource Setup\u003c/h2\u003e\n\u003cp\u003eNormally people use headers for larger C++ programs, though we\u0026rsquo;re going to create one just so we can see how to include it later in SWIG. Let\u0026rsquo;s called this file \u003ccode\u003efactorial.hpp\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#ifndef FACTORIAL_H\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#define FACTORIAL_H\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003estd\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003estring fact(\u003cspan style=\"color:#66d9ef\"\u003eunsigned\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e n);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#endif\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn order to get it the large number from C++ to Python. We are going to use \u003ccode\u003estd::string\u003c/code\u003e as the return of our \u003ccode\u003efact\u003c/code\u003e function.\u003c/p\u003e\n\u003cp\u003eHere is the source \u003ccode\u003efactorial.cpp\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026lt;gmpxx.h\u0026gt;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#include\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34;factorial.hpp\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003estd\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003estring fact(\u003cspan style=\"color:#66d9ef\"\u003eunsigned\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e n) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (n \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                n \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        mpz_class \u003cspan style=\"color:#a6e22e\"\u003eresult\u003c/span\u003e(n);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e (n \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                n\u003cspan style=\"color:#f92672\"\u003e--\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                result \u003cspan style=\"color:#f92672\"\u003e*=\u003c/span\u003e n;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e result.get_str(\u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e); \u003cspan style=\"color:#75715e\"\u003e// Base 10\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow that we have our C++ code, we need to create a swig template file called \u003ccode\u003efactorial.i\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e %module factorial\n %{\n #include \u0026#34;factorial.hpp\u0026#34;\n %}\n \n %include \u0026lt;std_string.i\u0026gt;\n %include \u0026#34;factorial.hpp\u0026#34;\n \n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eSince we\u0026rsquo;re returning a \u003ccode\u003estd::string\u003c/code\u003e we need to tell SWIG what that is. We do this through the \u003ccode\u003e\u0026lt;std_string.i\u0026gt;\u003c/code\u003e include.\u003c/p\u003e\n\u003cp\u003eWe can now ask SWIG to write the C++ code that will interface with Python. This will create the files  \u003ccode\u003efactorial_wrap.cxx\u003c/code\u003e and \u003ccode\u003efactorial.py\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eswig -c++ -python factorial.i\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"compilation-and-linkage\"\u003eCompilation and Linkage\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s compile our C++ code.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eg++ -O2 -fPIC -c factorial.cpp \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eFlag\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e-O2\u003c/td\u003e\n          \u003ctd\u003ePerform nearly all supported optimizations that don\u0026rsquo;t involve a space-speed tradeoff.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e-fPIC\u003c/td\u003e\n          \u003ctd\u003eCreate Position-Independent Code\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e-c\u003c/td\u003e\n          \u003ctd\u003eDon\u0026rsquo;t link at this time\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eTo compile \u003ccode\u003efactorial_wrap.cxx\u003c/code\u003e we need to include the directory where \u003ccode\u003ePython.h\u003c/code\u003e lives. You can find this by issuing the command \u003ccode\u003elocate Python.h\u003c/code\u003e. Below is where it is located on my system.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eg++ -O2 -fPIC -c factorial_wrap.cxx -I/home/user/.pyenv/versions/3.8.2/include/python3.8/\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFinally let\u0026rsquo;s create the needed shared object file by linking \u003ccode\u003efactorial.o\u003c/code\u003e, \u003ccode\u003efactorial_wrap.o\u003c/code\u003e, and the GNU MP libraries.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eg++ -O2 -fPIC -shared factorial.o factorial_wrap.o -lgmpxx -lgmp -o _factorial.so\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIt is important that our final output is called \u003ccode\u003e_\u003c/code\u003e + module_name.so\u003c/p\u003e\n\u003cp\u003eWe should at this time be able to open up \u003ccode\u003epython\u003c/code\u003e and import our function.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e factorial\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efactorial\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efact(\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you run into any errors, the \u003ca href=\"http://www.swig.org/Doc3.0/Python.html#Python_nn3\"\u003eSWIG Documentation\u003c/a\u003e is quite helpful.\u003c/p\u003e\n\u003cp\u003eIn order to not have to type out the compiling and linking commands every time, here is a  Makefile\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-makefile\" data-lang=\"makefile\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCC\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eg++\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCFLAGS\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e-O2 -fPIC -Wall\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePYTHON_PATH\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/home/user/.pyenv/versions/3.8.2/include/python3.8/\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eall\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e _factorial.so\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e_factorial.so\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e factorial.o factorial_wrap.o\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eCC\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eCFLAGS\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e -shared factorial.o factorial_wrap.o -lgmpxx -lgmp -o _factorial.so\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003efactorial_wrap.o\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e factorial_wrap.cxx\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eCC\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eCFLAGS\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e -c factorial_wrap.cxx -I\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003ePYTHON_PATH\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003efactorial.o\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e factorial.cpp\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eCC\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eCFLAGS\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e -c factorial.cpp\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003efactorial_wrap.cxx\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e factorial.i\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tswig -c++ -python factorial.i\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eclean\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\trm *.o *.so factorial_wrap.cxx factorial.py\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen you can call \u003ccode\u003emake clean\u003c/code\u003e to clean up everything and \u003ccode\u003emake\u003c/code\u003e to run all the individual compilation steps we did before.\u003c/p\u003e\n",
    "date_published": "2020.10.27",
    "tags": ["Python","C++"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/quickcgroups/",
    "url": "https://brandonrozek.com/blog/quickcgroups/",
    "title": "Quickly Creating CGroups to Limit CPU/Memory of Applications",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eRunning some data science algorithms can be CPU or memory intensive. To prevent the code from hogging system resources, we can use cgroups to set limits. This also is great for applications that you don\u0026rsquo;t quite trust.\u003c/p\u003e\n\u003ch2 id=\"defining-the-cgroup\"\u003eDefining the CGroup\u003c/h2\u003e\n\u003cp\u003eOn Ubuntu, we will have to install \u003ccode\u003ecgroup-tools\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install cgroup-tools\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLet\u0026rsquo;s create a cgroup called \u003ccode\u003elimitapp\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo cgcreate -a $USER -t $USER -g memory,cpu:limitapp\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can limit the RAM usage in this cgroup to 2GB. \u003ca href=\"https://wiki.archlinux.org/index.php/Cgroups#Ad-hoc_groups\"\u003eAccording to the Arch Linux Wiki\u003c/a\u003e, this limit only applies to RAM not SWAP.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#ae81ff\"\u003e2000000000\u003c/span\u003e \u0026gt; /sys/fs/cgroup/memory/limitapp/memory.limit_in_bytes\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAll processes belong to a cgroup. The majority of them belong to what is considered the root cgroup. By default, all groups have 1024 CPU shares. The amount of shares that you define in the cgroup determines the approximate percentage of CPU time that the cgroup will get if the system is congested.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s say that our cgroup can have 50% of the CPU time when the system is stressed:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#ae81ff\"\u003e512\u003c/span\u003e \u0026gt; /sys/fs/cgroup/cpu/limitapp/cpu.shares\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"assigning-applications-to-cgroups\"\u003eAssigning Applications to CGroups\u003c/h2\u003e\n\u003cp\u003eWe can assign an existing application to a cgroup. For example, let us assign \u003ccode\u003efirefox\u003c/code\u003e to our cgroup.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecgclassify -g memory,cpu:limitapp \u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003epidof firefox\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can start new processes in our cgroup. For example, let us start a python environment in our cgroup.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecgexec -g memory,cpu:limitapp python\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.10.22",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/bandwidthlimitingapps/",
    "url": "https://brandonrozek.com/blog/bandwidthlimitingapps/",
    "title": "Bandwidth Limiting Applications",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhether it\u0026rsquo;s web scraping or testing low latency connections, we want to be able to bandwidth limit certain applications. I\u0026rsquo;ve written about \u003ca href=\"/blog/limitbandwidth/\"\u003erate limiting network interfaces\u003c/a\u003e before. This post focuses on the application level instead.\u003c/p\u003e\n\u003cp\u003eFirst if the application has an option to rate limit, then we should prefer that. Here are the ways you can rate limit in some common terminal applications. Let\u0026rsquo;s say you set the bandwidth limit as \u003ccode\u003e$max_kbps\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ersync --bwlimit $max_kbps ...\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl --limit-rate $max_kbps ...\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewget --limit-rate $max_kbps ...\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf the application you want to rate limit does not have a convenient flag and is dynamically linked, then we can use the application \u003ca href=\"https://github.com/mariusae/trickle\"\u003e\u003ccode\u003etrickle\u003c/code\u003e\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etrickle -s -d $max_kbps -u $max_kbps\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eFlag\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-s\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eStandalone Mode\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-d\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eDownload Bandwidth Consumption Rate (KB/s)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-u\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eUpload Bandwidth Consumption Rate (KB/s)\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eTrickle works by replacing the socket library that is dynamically linked.\u003c/p\u003e\n",
    "date_published": "2020.10.22",
    "tags": ["Linux","Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/appimage/",
    "url": "https://brandonrozek.com/blog/appimage/",
    "title": "Deploying Binaries to other Linux Distros using Appimage",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOne way to distribute to different Linux distributions is to compile the source under each of them and distribute it separately.  This can be a pain to manage if you target multiple distributions and multiple versions of those distributions. Instead, let\u0026rsquo;s take a look at AppImage. This allows us to package up our binaries and shared libraries under one file which we can distribute.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/appimage-architecture-overview.svg\" alt=\"AppImage Architecture Diagram\"\u003e\u003c/p\u003e\n\u003cp\u003eSource: \u003ca href=\"https://docs.appimage.org/reference/architecture.html\"\u003ehttps://docs.appimage.org/reference/architecture.html\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eIt works by packaging up the application in a SquashFS which is then mounted during runtime using FUSE. The project recommends that the AppImage is packaged on an older system, like an Ubuntu LTS or CentOS. Newer systems are often backwards compatible but not the other way around. Let\u0026rsquo;s get started!\u003c/p\u003e\n\u003ch2 id=\"setting-up-the-appimage\"\u003eSetting up the AppImage\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s create an AppImage based on the application Gqrx. The following structure will be common for all binary distribution packages:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egqrx.AppDir/\n  AppRun\n  icon.png\n  gqrx.desktop\n  usr/\n    bin/\n      gqrx\n    etc/\n      # Config files\n    lib/\n      # Shared libraries\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe \u003ccode\u003eAppRun\u003c/code\u003e executable comes from the \u003ca href=\"https://github.com/AppImage/AppImageKit/releases\"\u003eAppImage Releases Page\u003c/a\u003e. While you\u0026rsquo;re there, you should also download \u003ccode\u003eappimagetool\u003c/code\u003e as we\u0026rsquo;ll be using that soon.\u003c/p\u003e\n\u003cp\u003eThe \u003ccode\u003e.desktop\u003c/code\u003e file needs to be a valid \u003ca href=\"/blog/linuxdesktopicons/\"\u003eLinux Desktop Icon\u003c/a\u003e. Some things to take note about the desktop file:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThe icon is assumed to be a \u003ccode\u003e.png\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eThe executable is assumed to be located in \u003ccode\u003e/usr/bin/\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eMake sure to define the \u003ca href=\"https://specifications.freedesktop.org/menu-spec/latest/apa.html\"\u003e\u003ccode\u003eCategories\u003c/code\u003e\u003c/a\u003e field, the \u003ccode\u003eappimagetool\u003c/code\u003e complained when I didn\u0026rsquo;t.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eHere is an example desktop file.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e[Desktop Entry]\nType=Application\nName=Gqrx\nIcon=icon\nExec=gqrx\nCategories=Development;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eCopy over \u003ccode\u003egqrx\u003c/code\u003e executable over to the AppDir:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport gqrx_loc\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003ewhich gqrx\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecp $gqrx_loc gqrx.AppDir/usr/bin/\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"finding-needed-shared-libraries\"\u003eFinding Needed Shared Libraries\u003c/h3\u003e\n\u003cp\u003eThis part is the most time consuming of this whole process. Most of the shared libraries that the application depends on needs to be added to the \u003ccode\u003eusr/lib\u003c/code\u003e folder. Except for the lowest level libraries.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s first take a look at all the libraries we need:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport all_libraries\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eldd $gqrx_loc | awk \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;{ print $1 }\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho $all_libraries\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen let\u0026rsquo;s grab the \u003ca href=\"https://raw.githubusercontent.com/AppImage/pkg2appimage/master/excludelist\"\u003e\u003ccode\u003eexcludelist\u003c/code\u003e from AppImage\u003c/a\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewget https://raw.githubusercontent.com/AppImage/pkg2appimage/master/excludelist\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow let\u0026rsquo;s filter our library output to take away the lowest level libraries:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexport filtered_libraries\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003eecho $all_libraries | grep -vwf excludelist\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho $filtered_libraries\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf this filter doesn\u0026rsquo;t work, then you might need to make sure each library is listed on its own line.\u003c/p\u003e\n\u003cp\u003eFinally, let\u0026rsquo;s save the locations of these libraries to \u003ccode\u003elibrary_locations.txt\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eldd $gqrx_loc | grep $filtered_libraries | awk \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;{ print $3 }\u0026#39;\u003c/span\u003e \u0026gt; library_locations.txt\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecat library_locations.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eCopy over all the libraries\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003exargs -a library_locations.txt -L \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e -I @ cp @ gqrx.AppDir/usr/lib/\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf there are any libraries that you need get dynamically injected during runtime, you will have to copy those over as well.\u003c/p\u003e\n\u003cp\u003eFor those using Qt plugins, those live within the \u003ccode\u003e/usr/bin\u003c/code\u003e directory. For example \u003ccode\u003elibqxcb.so\u003c/code\u003e lives in \u003ccode\u003e/usr/bin/platforms/libqxcb.so\u003c/code\u003e.\u003c/p\u003e\n\u003ch3 id=\"creating-the-appimage\"\u003eCreating the AppImage\u003c/h3\u003e\n\u003cp\u003eFinally we can use the \u003ccode\u003eappimagetool\u003c/code\u003e command to create the AppImage:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e./appimagetool-x86_64.AppImage gqrx.AppDir/\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhich will generate a \u003ccode\u003egqrx-appimage-x86_64.AppImage\u003c/code\u003e file.\u003c/p\u003e\n\u003cp\u003eNow for the moment of true, copy that file over to another Linux distribution and see if it executes appropriately.  If it complains about shared libraries missing, then you should go back to the previous system and add that library in.\u003c/p\u003e\n\u003cp\u003eIf it complains about Illegal instructions, then there is probably either a low level library that needs to be removed, or the CPU instruction set between the two computers differ.\u003c/p\u003e\n",
    "date_published": "2020.10.19",
    "tags": ["Packaging","Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/reverseonehotencode/",
    "url": "https://brandonrozek.com/blog/reverseonehotencode/",
    "title": "Reverse One-Hot Encode",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLet\u0026rsquo;s say that you have a dataset that is one hot encoded like the following observation:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e numpy \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e np\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eobs \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e np\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003earray([\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e       \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e])\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe easiest way to reverse one-hot encode the structure, is to take the \u003ccode\u003eargmax\u003c/code\u003e of the observation.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ereverse_encoding \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e np\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eargmax(obs)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 13\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.10.11",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/tcopython/",
    "url": "https://brandonrozek.com/blog/tcopython/",
    "title": "Tail Call Optimization in Python",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn a standard recursive function, a new stack frame is created every time a recursive call is made. This can lead to bad memory performance and as a protective measure, some programming languages have a maximum stack frame limit.\u003c/p\u003e\n\u003cp\u003eThe following example is in Python:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efactorial\u003c/span\u003e(n):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e factorial(n\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u0026gt;\u0026gt;\u003c/span\u003e factorial(\u003cspan style=\"color:#ae81ff\"\u003e1000\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTraceback (most recent call last):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  File \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026lt;stdin\u0026gt;\u0026#34;\u003c/span\u003e, line \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003emodule\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  File \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026lt;stdin\u0026gt;\u0026#34;\u003c/span\u003e, line \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e, \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e factorial\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  File \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026lt;stdin\u0026gt;\u0026#34;\u003c/span\u003e, line \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e, \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e factorial\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  File \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026lt;stdin\u0026gt;\u0026#34;\u003c/span\u003e, line \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e, \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e factorial\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  [Previous line repeated \u003cspan style=\"color:#ae81ff\"\u003e995\u003c/span\u003e more times]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  File \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026lt;stdin\u0026gt;\u0026#34;\u003c/span\u003e, line \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e, \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e factorial\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRecursionError\u003c/span\u003e: maximum recursion depth exceeded \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e comparison\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"implementation\"\u003eImplementation\u003c/h2\u003e\n\u003cp\u003eTo get around this in Python, we need some sort of \u003ca href=\"https://en.wikipedia.org/wiki/Tail_call#Through_trampolining\"\u003etrampoline\u003c/a\u003e, or way to exit part the stack and call the recursive function again. This technique is often used in Tail Call Optimization, and we can do this in Python using exceptions. This code is heavily inspired by Crutcher Dunnavant\u0026rsquo;s \u003ca href=\"https://code.activestate.com/recipes/474088-tail-call-optimization-decorator/\"\u003ecode snippet\u003c/a\u003e from 2006.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003ePython Decorator that enables tail call\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eoptimization through Exception Trampolines.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e dataclasses \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e dataclass, field\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e typing \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e Any, Dict, List\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e functools\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e inspect\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@dataclass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eTailRecurseException\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eException\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;Exception to enable tail call recursion.\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    args: List[Any] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e field(default_factory\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003elist)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    kwargs: Dict[Any, Any] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e field(default_factory\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003edict)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etail_call\u003c/span\u003e(func):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    Decorator that performs tail call optimization.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    Notes\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    =====\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    Works by throwing an exception to exit the stack\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    when the function sees itself as its grandparent in\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    the stack trace. It then calls itself with its new\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    arguments.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003e@functools.wraps\u003c/span\u003e(func)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erecurse\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eargs, \u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003ekwargs):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        frame \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e inspect\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ecurrentframe()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e frame\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ef_back \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e \\\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e           \u003cspan style=\"color:#f92672\"\u003eand\u003c/span\u003e frame\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ef_back\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ef_back \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e \\\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e           \u003cspan style=\"color:#f92672\"\u003eand\u003c/span\u003e frame\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ef_back\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ef_back\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ef_code \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e frame\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ef_code:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e TailRecurseException(args, kwargs)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eTrue\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003etry\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e func(\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eargs, \u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003ekwargs)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eexcept\u003c/span\u003e TailRecurseException \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e exception:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                args \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e exception\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eargs\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                kwargs \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e exception\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ekwargs\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e recurse\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"example-factorial\"\u003eExample: Factorial\u003c/h2\u003e\n\u003cp\u003eNow let\u0026rsquo;s redefine \u003ccode\u003efactorial\u003c/code\u003e in a tail-call way:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@tail_call\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efactorial\u003c/span\u003e(n, acc\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e acc\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e factorial(n \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, n \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e acc)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor the moment of truth\u0026hellip;\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u0026gt;\u0026gt;\u003c/span\u003e factorial(\u003cspan style=\"color:#ae81ff\"\u003e1000\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"example-fibonacci-numbers\"\u003eExample: Fibonacci Numbers\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@tail_call\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efib\u003c/span\u003e(n, a\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, b\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e a\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e b\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e fib(n \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, b, a \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e b)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u0026gt;\u0026gt;\u003c/span\u003e fib(\u003cspan style=\"color:#ae81ff\"\u003e1000\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eYou can install the package via pypi:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install tail-recurse\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIt is also available on \u003ca href=\"https://github.com/Brandon-Rozek/tail-recurse/\"\u003eGithub\u003c/a\u003e.\u003c/p\u003e\n",
    "date_published": "2020.10.11",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/managepythonapps/",
    "url": "https://brandonrozek.com/blog/managepythonapps/",
    "title": "Manage Python Applications",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve recently discovered an application called \u003ca href=\"https://pipxproject.github.io/pipx/\"\u003e\u003ccode\u003epipx\u003c/code\u003e\u003c/a\u003e which allows one to install and run Python applications in isolated environments. I think of it as a package similar to \u003ccode\u003eapt\u003c/code\u003e, but keeps the packages nice and isolated from one another.\u003c/p\u003e\n\u003cp\u003eTo Install:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install python3-venv pipx\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epipx ensurepath\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBy default, it will create the virtualenvs in \u003ccode\u003e~/.local/pipx\u003c/code\u003e and drop executables in \u003ccode\u003e~/.local/bin\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eInstall \u003ccode\u003ediceware\u003c/code\u003e using \u003ccode\u003epipx\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epipx install diceware\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eList the virtual environments maintained by \u003ccode\u003epipx\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epipx list\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUpgrade a package:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epipx upgrade diceware\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAdd additional dependencies into a package\u0026rsquo;s virtual environment:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epipx inject package dependency\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.10.11",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/autopurgedocker/",
    "url": "https://brandonrozek.com/blog/autopurgedocker/",
    "title": "Auto Purge Old Docker Images",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI use \u003ca href=\"https://github.com/containrrr/watchtower\"\u003eWatchtower\u003c/a\u003e to automatically update the docker images I use. After leaving it for several months, I\u0026rsquo;ve realized that I have been storing over 100GB of old docker images. I needed a way to automatically purge  old images and \u003ca href=\"https://opensource.com/article/20/7/systemd-timers\"\u003eSystemd Timers\u003c/a\u003e is the solution.\u003c/p\u003e\n\u003cp\u003eFirst it\u0026rsquo;s useful to know the docker command that purges unused images that are older than 24 hours old.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker image prune -fa --filter \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;until=24h\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen we can create a oneshot service file that will describe its dependencies and descriptions for Systemd to manage. This file is \u003ccode\u003e/etc/systemd/system/docker-purge.service\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yml\" data-lang=\"yml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003eUnit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eDescription=Purge Docker Images Older than 24 Hours\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eRequires=docker.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eWants=docker-purge.timer\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003eService]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eType=oneshot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eExecStart=/usr/bin/docker image prune -fa --filter \u0026#34;until=24h\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003eInstall]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eWantedBy=multi-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow we can create the systemd timer that will hook to this service. This is \u003ccode\u003e/etc/systemd/system/docker-purge.timer\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003eUnit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eDescription=Purge Docker Images Older than 24 Hours\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eRequires=docker-purge.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003eTimer]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eUnit=docker-purge.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eOnCalendar=*-*-* 00:00:00\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eAccuracySec=24h\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003eInstall]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eWantedBy=timers.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis tells Systemd to run the service every day if enabled/started, though not necessarily at midnight. Systemd will schedule a time to run the service within the \u003ccode\u003eAccuracySec\u003c/code\u003e parameter. That is, it will schedule a time to run sometime everyday.\u003c/p\u003e\n\u003cp\u003eFinally, let\u0026rsquo;s enable and start the timer.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl enable docker-purge\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl start docker-purge\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can check the time that docker-purge is scheduled to run next by asking Systemd to list its timers.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemctl list-timers\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you want to play around with the \u003ccode\u003eOnCalendar\u003c/code\u003e parameter.  A useful command is\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystmed-analyze calendar --iterations\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$N \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$TIME\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhere you replace \u003ccode\u003e$N\u003c/code\u003e with an integer, and \u003ccode\u003e$TIME\u003c/code\u003e with your sample time string.\u003c/p\u003e\n",
    "date_published": "2020.09.28",
    "tags": ["Containers"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/creatingqrcodesfromterminal/",
    "url": "https://brandonrozek.com/blog/creatingqrcodesfromterminal/",
    "title": "Creating QR Codes from the Terminal",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWith the tool \u003ccode\u003eqrencode\u003c/code\u003e, it is surprisingly simple to create a QR code.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://brandonrozek.com\u0026#34;\u003c/span\u003e | qrencode -t ansiutf8\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/20200926221423.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eAfter viewing it on your terminal, you can save it to a PNG file\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://brandonrozek.com\u0026#34;\u003c/span\u003e | qrencode -t png -o qrcode.png\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.09.26",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/launchappsthroughterminal/",
    "url": "https://brandonrozek.com/blog/launchappsthroughterminal/",
    "title": "Launch Apps through the Terminal",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eNormally when you launch an application through the terminal, the standard output appears, and closing the terminal closes the application.\u003c/p\u003e\n\u003ch2 id=\"using-systemd\"\u003eUsing \u003ccode\u003esystemd\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://fosstodon.org/@ralismark\"\u003eTem Tem\u003c/a\u003e recently \u003ca href=\"https://fosstodon.org/@ralismark/108266728217245129\"\u003etooted\u003c/a\u003e\na \u003ca href=\"https://www.ralismark.xyz/posts/systemd-run\"\u003eblog post\u003c/a\u003e they wrote on replacing \u003ccode\u003enohup\u003c/code\u003e with \u003ccode\u003esystemd-run\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eTo run a graphical application it\u0026rsquo;s as easy as:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemd-run --user application\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you want to see any of the application terminal output,\nthen when the service is running you can check the status\nlike any other systemd service.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemd --user status application\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNote that the current directory information is not known\nto \u003ccode\u003esystemd-run\u003c/code\u003e. Therefore, if you\u0026rsquo;ll need to specify\nabsolute as opposed to relative paths. For example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemd-run --user okular \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$PWD\u003cspan style=\"color:#e6db74\"\u003e/document.pdf\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eCheck out Tem Tem\u0026rsquo;s \u003ca href=\"https://www.ralismark.xyz/posts/systemd-run\"\u003eblog post\u003c/a\u003e for more on \u003ccode\u003esystemd-run\u003c/code\u003e!\u003c/p\u003e\n\u003ch2 id=\"using-nohup-legacy\"\u003eUsing \u003ccode\u003enohup\u003c/code\u003e (Legacy)\u003c/h2\u003e\n\u003cp\u003eWhen the terminal closes, it sends a hangup signal to all of the processes it manages.\nThe \u003ccode\u003enohup\u003c/code\u003e command allows applications to run regardless of any hangups sent.\nCombine that with making it a background task,\nand you have a quick and easy way to launch applications through the terminal.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enohup application \u0026gt; /dev/null \u0026amp;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.09.26",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/bashpartialargparse/",
    "url": "https://brandonrozek.com/blog/bashpartialargparse/",
    "title": "Partial Argument Parse and Passing in Bash",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLet\u0026rsquo;s say we want to augment an existing terminal command (like for example \u003ccode\u003ewget\u003c/code\u003e). We then want to be able to add or edit command line options. The rest of this post provides an example that hopefully you can use in your bash script.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Custom help function\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eshow_help\u003cspan style=\"color:#f92672\"\u003e()\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Usage: custom_command [arguments]\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;    --name \u0026lt;name\u0026gt;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;    --flag_example\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;    \u0026lt;additional arguments to be passed along\u0026gt;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  exit \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Defaults for our custom flags or parameters.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ename\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eflag_example\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Loop through and take out our custom parameters\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# from the parameter list.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ei\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enumargs\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$#\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e test $i -lt \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$numargs\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e in\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;--help\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      show_help\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      ;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;--name\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      shift\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      name\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      ;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;--flag_example\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      flag_example\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      ;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    *\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      set -- \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$@\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      ;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eesac\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  shift\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  i\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$((\u003c/span\u003ei+1\u003cspan style=\"color:#66d9ef\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Do something here using our custom parameters\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Pass our non-custom parameters to the application\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewget \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$@\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.09.07",
    "tags": ["Bash"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/xvfb/",
    "url": "https://brandonrozek.com/blog/xvfb/",
    "title": "Ignore graphical applications with Xvfb",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eX Virtual Framebuffer (Xvfb) is a great quick application to run applications that expect a \u003ccode\u003eX11\u003c/code\u003e server, but you don\u0026rsquo;t care to see the visual output.\u003c/p\u003e\n\u003cp\u003eTo install:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install xvfb\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo run:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003exvfb-run application\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor more customization, I hear that it\u0026rsquo;s better to use \u003ca href=\"https://web.archive.org/web/20200926082251/https://xpra.org/trac/wiki/Xdummy\"\u003expra with Xdummy\u003c/a\u003e. I haven\u0026rsquo;t tried this myself yet, but if you experience issues with the application expecting \u003ccode\u003erandr\u003c/code\u003e or \u003ccode\u003eglx\u003c/code\u003e extensions, give it a shot.\u003c/p\u003e\n",
    "date_published": "2020.09.07",
    "tags": ["X11"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/splittingfiles/",
    "url": "https://brandonrozek.com/blog/splittingfiles/",
    "title": "Splitting Files to Circumvent Size Limits",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLet\u0026rsquo;s say you want to transfer file(s) over to someone and there is a limit the size you can transfer over. Maybe because of the physical medium or the online service you\u0026rsquo;re using. You can make use of the terminal tool \u003ccode\u003esplit\u003c/code\u003e in order to get the chunks over there and then use \u003ccode\u003ecat \u003c/code\u003e to combine it back to one file.\u003c/p\u003e\n\u003ch2 id=\"create-a-single-file\"\u003eCreate a Single File\u003c/h2\u003e\n\u003cp\u003eSkip to the next section if you only want to transfer a single file.\u003c/p\u003e\n\u003cp\u003eOtherwise, let\u0026rsquo;s say you have a folder of files named \u003ccode\u003eDocuments\u003c/code\u003e. First, let\u0026rsquo;s create a gzipped compressed archive that contains everything.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etar -czf documents.tar.gz Documents\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"splitting-the-file\"\u003eSplitting the File\u003c/h2\u003e\n\u003cp\u003eNext let\u0026rsquo;s split the file into 1 GB parts.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esplit -b 1GB -d documents.tar.gz PART\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen transfer the files labeled PARTX where X is the part number over\u0026hellip;.\u003c/p\u003e\n\u003ch2 id=\"re-combining\"\u003eRe-combining\u003c/h2\u003e\n\u003cp\u003eOnce the files are transfered, we use \u003ccode\u003ecat\u003c/code\u003e to bring the files back together.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecat PART* \u0026gt; documents.tar.gz\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.09.07",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/startgraphicalappsnoforwarding/",
    "url": "https://brandonrozek.com/blog/startgraphicalappsnoforwarding/",
    "title": "Starting Graphical Applications Remotely without X-Forwarding",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOne of my favorite casual gaming setups is to use a slim laptop on my couch while streaming from my desktop using Steam Remote Play.  The downside though is that unless I want to get up, Steam needs to be already running on my desktop. This post describes how I start steam remotely from my couch so that I can stream my games without having to get up and set it up on my desktop first.\u003c/p\u003e\n\u003cp\u003eThis requires you to be already logged into a graphical session on the computer you want to stream from.\u003c/p\u003e\n\u003cp\u003eOn Linux, there is a DISPLAY environmental variable which tells X11 where to show the application. By default (at least on Ubuntu), the display starts as \u003ccode\u003e:0\u003c/code\u003e and moves up in count. We can make use of this fact, to start steam on a X11 server instance that is already running so that we don\u0026rsquo;t have to do any X11 forwarding.\u003c/p\u003e\n\u003cp\u003eOn the client computer,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003essh desktop \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;DISPLAY=:0 nohup steam\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003enohup\u003c/code\u003e allows the application to not terminate when you press \u003ccode\u003eCTRL-C\u003c/code\u003e from your client computer.\u003c/p\u003e\n\u003cp\u003eUpdate: In light of \u003ca href=\"/blog/launchappsthroughterminal/\"\u003ediscovering \u003ccode\u003esystemd-run\u003c/code\u003e\u003c/a\u003e, I recommand you\ndo the following instead:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003essh desktop \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;DISPLAY=:0 systemd-run --user steam\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.09.07",
    "tags": ["Linux","SSH"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/ipaddressesinpython/",
    "url": "https://brandonrozek.com/blog/ipaddressesinpython/",
    "title": "How to get list of IP Addresses in Python",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://stackoverflow.com/users/2436840/elemag\"\u003eElemag\u003c/a\u003e on gave a quick solution on StackOverflow on \u003ca href=\"https://stackoverflow.com/questions/270745/how-do-i-determine-all-of-my-ip-addresses-when-i-have-multiple-nics\"\u003ehow to determine IP addresses with multiple NICS\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eHis one-line solution:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e netifaces\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eip_addresses \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [netifaces\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eifaddresses(iface)[netifaces\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eAF_INET][\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e][\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;addr\u0026#39;\u003c/span\u003e] \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e iface \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e netifaces\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003einterfaces() \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e netifaces\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eAF_INET \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e netifaces\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eifaddresses(iface)]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eExpanded out to see what is happening\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e netifaces\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003einterface_list \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e netifaces\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003einterfaces()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Get addresses, netmask, etc. information \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eaddress_entries \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (netifaces\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eifaddresses(iface) \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e iface \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e interface_list)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Only pay attention to ipv4 address types\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eipv4_address_entries \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (address[netifaces\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eAF_INET] \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e address \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e address_entries \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e netifaces\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eAF_INET \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e address)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Since multiple addresses can be associated, only look at the first ip address\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eipv4_addresses \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [address[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e][\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;addr\u0026#39;\u003c/span\u003e] \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e address \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e ipv4_address_entries]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can easily adjust this to ask for IPv6 addresses by using \u003ccode\u003enetifaces.AF_INET6\u003c/code\u003e instead.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e netifaces\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003einterface_list \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e netifaces\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003einterfaces()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Get addresses, netmask, etc. information \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eaddress_entries \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (netifaces\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eifaddresses(iface) \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e iface \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e interface_list)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Only pay attention to ipv6 address types\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eipv6_address_entries \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (address[netifaces\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eAF_INET6] \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e address \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e address_entries \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e netifaces\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eAF_INET6 \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e address)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Since multiple addresses can be associated, only look at the first ip address\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eipv6_addresses \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [address[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e][\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;addr\u0026#39;\u003c/span\u003e] \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e address \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e ipv6_address_entries]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.09.07",
    "tags": ["Python","Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/jupyterwithpyenv/",
    "url": "https://brandonrozek.com/blog/jupyterwithpyenv/",
    "title": "Jupyter with Pyenv",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI enjoy both managing my Python versions with \u003ca href=\"/blog/pyenv/\"\u003epyenv\u003c/a\u003e and the literate programming environment \u003ca href=\"https://jupyter.org/\"\u003eJupyter lab\u003c/a\u003e. Luckily we can easily manage Python virtual environments via iPython kernels.\u003c/p\u003e\n\u003cp\u003eWe\u0026rsquo;re going to start off with our base Python interpretor\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv install 3.8.4\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv shell 3.8.4\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen we\u0026rsquo;re going to install the \u003ccode\u003eipykernel\u003c/code\u003e package.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install --upgrade pip\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install ipykernel\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNext we\u0026rsquo;re going to create a virtual environment where JupyterLab will live.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv virtualenv jupyter\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv activate jupyter\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install jupyterlab\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWith that, we can now create arbitrary environments and add them as a kernel.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv deactivate\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv virtualenv tensorflow\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv activate tensorflow\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install tensorflow\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eipython kernel install --user --name tensorflow\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.07.21",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/asynccallbacks/",
    "url": "https://brandonrozek.com/blog/asynccallbacks/",
    "title": "Quick Python: Async Callbacks",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve written a post on using \u003ca href=\"/blog/pysubscribepattern/\"\u003ecallbacks in Python\u003c/a\u003e. Though to add callbacks to \u003ccode\u003easyncio\u003c/code\u003e functions, you\u0026rsquo;ll have to interact with the loop object directly. Replace the emit function in the previous post with the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eApplication\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# ...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eemit\u003c/span\u003e(self, message):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e callback \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ecallbacks:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e asyncio\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eiscoroutine(callback):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                loop \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e asyncio\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eget_running_loop()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                loop\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ecall_soon(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    functools\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epartial(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        asyncio\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eensure_future,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        callback(message)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                callback(message)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.07.11",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/firefoxprivacyconfigs/",
    "url": "https://brandonrozek.com/blog/firefoxprivacyconfigs/",
    "title": "Firefox Privacy Configuration",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eFirefox has a great number of configurable options when it comes to privacy. I first discovered this as I was browsing \u003ca href=\"https://www.privacytools.io/browsers/\"\u003eprivacytools.io\u003c/a\u003e recommendations. Mozilla also has a \u003ca href=\"https://wiki.mozilla.org/Privacy/Privacy_Task_Force/firefox_about_config_privacy_tweeks\"\u003eprivacy task force\u003c/a\u003e documenting some these options. I later learned that there is a website called \u003ca href=\"https://ffprofile.com/\"\u003eFirefox Profilemaker\u003c/a\u003e that takes the user step by step creating a privacy preserving profile.\u003c/p\u003e\n\u003cp\u003eHere are the list of settings in \u003ccode\u003eabout:config\u003c/code\u003e that I found most interesting,\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eFlag\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eprivacy.resistFingerprinting\u003c/td\u003e\n          \u003ctd\u003eAttempts to \u003ca href=\"https://wiki.mozilla.org/Security/Fingerprinting\"\u003eresist fingerprinting\u003c/a\u003e.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eprivacy.firstparty.isolate\u003c/td\u003e\n          \u003ctd\u003eIsolates cookies to the first party domain to prevent tracking across domains.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003enetwork.IDN_show_punycode\u003c/td\u003e\n          \u003ctd\u003eRender internationalized domain name (IDN) as Punycode equivalent to help against \u003ca href=\"https://en.wikipedia.org/wiki/IDN_homograph_attack\"\u003ephishing attacks\u003c/a\u003e.\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e\u003ca href=\"https://holdintegrity.com/checker\"\u003eHold Integrity released a tool\u003c/a\u003e to check IDNs and see if there are any look alikes domains registered on the Internet.\u003c/p\u003e\n",
    "date_published": "2020.06.26",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/gpgagentasssh/",
    "url": "https://brandonrozek.com/blog/gpgagentasssh/",
    "title": "GPG Agent as SSH Agent",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eGPG Agent has the ability to act as a SSH Agent. This allows the use of Authentication keys on Smartcards to be used with SSH as well.\u003c/p\u003e\n\u003cp\u003eFirst we need to enable SSH support in GPG Agent,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;enable-ssh-support\u0026#34;\u003c/span\u003e \u0026gt;\u0026gt; ~/.gnupg/gpg-agent.conf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen we need to specify an environmental variable for the SSH Daemon to use GPG Agent\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;export SSH_AUTH_SOCK=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003egpgconf --list-dirs agent-ssh-socket\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u0026gt;\u0026gt; ~/.bashrc\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you want it to be active immediately, then source the bashrc,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esource ~/.bashrc\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you have a smartcard plugged in, then you should be able to see it via the GPG agent\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003essh-add -l\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.06.14",
    "tags": ["SSH","GPG/PGP"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/extract/",
    "url": "https://brandonrozek.com/blog/extract/",
    "title": "Extract All the Things",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://www.networkworld.com/author/Sandra-Henry_Stocker/\"\u003eSandra Henry-Stocker\u003c/a\u003e from Network World wrote a \u003ca href=\"https://www.networkworld.com/article/3244007/extracting-from-compressed-files-on-linux.html\"\u003egreat post\u003c/a\u003e on how to standardize extracting files on Linux. It\u0026rsquo;s a shell script that works so well, that I placed it in my \u003ca href=\"https://brandonrozek.com/blog/customexec/\"\u003e\u003ccode\u003e~/.local/bin\u003c/code\u003e directory\u003c/a\u003e in order to call upon it at any time. Here\u0026rsquo;s part of it, check out the post for more.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e -f $1 \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e ; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e $1 in\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    *.tar.bz2\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e  tar xjf $1   ;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    *.tar.gz\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e   tar xzf $1   ;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    *.tar.xz\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e   tar zxvf $1  ;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    *.bz2\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e      bunzip2 $1   ;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    *.rar\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e      rar x $1     ;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    *.gz\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e       gunzip $1    ;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    *.tar\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e      tar xf $1    ;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    *.tbz2\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e     tar xjf $1   ;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    *.tgz\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e      tar xzf $1   ;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    *.xz\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e       xz -d $1     ;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    *.zip\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e      unzip $1     ;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    *.Z\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e        uncompress $1;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    *\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e          echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;contents of \u0026#39;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#39; cannot be extracted\u0026#34;\u003c/span\u003e ;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eesac\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#39;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#39; is not recognized as a compressed file\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.06.14",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/exportpydecorator/",
    "url": "https://brandonrozek.com/blog/exportpydecorator/",
    "title": "Quick Python: Export Decorator",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eA great \u003ca href=\"https://stackoverflow.com/a/35710527\"\u003eStackOverflow post\u003c/a\u003e by \u003ca href=\"https://stackoverflow.com/users/541136/aaron-hall\"\u003eAaron Hall\u003c/a\u003e that shows how you can create an \u003ccode\u003eexport\u003c/code\u003e decorator in order to not have to specify all the names you want to expose via \u003ca href=\"/blog/pythonall/\"\u003e\u003ccode\u003e__all__\u003c/code\u003e\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThe Decorator:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e sys\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eexport\u003c/span\u003e(fn):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    mod \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e sys\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emodules[fn\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e__module__]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e hasattr(mod, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;__all__\u0026#39;\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        mod\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e__all__\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend(fn\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e__name__)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        mod\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e__all__ \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [fn\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e__name__]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e fn\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUsage:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e__all__ \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@export\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e# otherwise __all__ = [\u0026#39;test\u0026#39;]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etest\u003c/span\u003e():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;test\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.06.14",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/tokindle/",
    "url": "https://brandonrozek.com/blog/tokindle/",
    "title": "PDF To Kindle",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis post will outline a process I take in order to get content from a website onto my Kindle.\u003c/p\u003e\n\u003ch2 id=\"suggested-software\"\u003eSuggested Software\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eTo save a webpage as a PDF, we will use \u003ca href=\"https://weasyprint.org/\"\u003eWeasyPrint\u003c/a\u003e.\u003c/li\u003e\n\u003cli\u003eTo convert the PDF into a more Kindle readable format, including converting math type properly, we\u0026rsquo;re going to use \u003ca href=\"https://www.willus.com/k2pdfopt/\"\u003ek2pdfopt\u003c/a\u003e.\u003c/li\u003e\n\u003cli\u003eFinally we\u0026rsquo;re going to use \u003ca href=\"https://calibre-ebook.com/\"\u003eCalibre\u003c/a\u003e to copy metadata and convert to an ebook format.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install weasyprint k2pdfopt calibre\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"process\"\u003eProcess\u003c/h2\u003e\n\u003cp\u003eNow I\u0026rsquo;ll show how we can take the \u003ca href=\"http://noiseprotocol.org/noise.html\"\u003eNoise Protocol specification\u003c/a\u003e and send it to the Kindle.\u003c/p\u003e\n\u003cp\u003eFirst let\u0026rsquo;s download the page as a PDF\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eweasyprint https://noiseprotocol.org/noise.html noise.pdf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen let\u0026rsquo;s use \u003ccode\u003ek2pdfopt\u003c/code\u003e to convert the PDF to a more Kindle friendly format,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ek2pdfopt noise.pdf -ui- -x\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will produce the file \u003ccode\u003enoise_k2opt.pdf\u003c/code\u003e, but sadly without the metadata. We can copy that over,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eebook-meta noise.pdf --to-opf temp.opf \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003eebook-meta noise_k2opt.pdf --from-opf temp.opf \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003erm temp.opf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFinally we can convert it to a Kindle friendly file format.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eebook-convert noise_k2opt.pdf noise.azw3\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will give us \u003ccode\u003enoise.azw3\u003c/code\u003e which we can then transfer over to the Kindle.\u003c/p\u003e\n",
    "date_published": "2020.06.14",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/gpgcard/",
    "url": "https://brandonrozek.com/blog/gpgcard/",
    "title": "GPG Card",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI have a Yubikey hardware token and one of my favorite use cases is the GPG support. This gives you the use cases of signing, encrypting, and authenticating all in one module. This post will describe quickly setting it up.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egpg --card--edit\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo edit the keys we need to be in admin mode.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egpg/card\u0026gt; admin\nAdmin commands are allowed\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFirst thing that\u0026rsquo;ll be good to configure is the name of the cardholder.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egpg/card\u0026gt; name\nCardholder\u0026#39;s surname: Rozek\nCardholder\u0026#39;s given name: Brandon\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eYou can also set the language preferences.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egpg/card\u0026gt; lang\nLanguage preferences: en\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow to configure the three different keys, we need to decide on an algorithm. There is a great \u003ca href=\"https://blog.cloudflare.com/a-relatively-easy-to-understand-primer-on-elliptic-curve-cryptography/\"\u003eblog post by Cloudflare\u003c/a\u003e describing RSA and ECC in detail. Including their pros and cons.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egpg/card\u0026gt; key-attr\nChanging card key attribute for: Signature key\nPlease select what kind of key you want:\n   (1) RSA\n   (2) ECC\nYour selection? 1\nWhat keysize do you want? (2048) 4096\nThe card will now be re-configured to generate a key of 4096 bits\nChanging card key attribute for: Encryption key\nPlease select what kind of key you want:\n   (1) RSA\n   (2) ECC\nYour selection? 1\nWhat keysize do you want? (2048) 4096\nThe card will now be re-configured to generate a key of 4096 bits\nChanging card key attribute for: Authentication key\nPlease select what kind of key you want:\n   (1) RSA\n   (2) ECC\nYour selection? 1\nWhat keysize do you want? (2048) 4096\nThe card will now be re-configured to generate a key of 4096 bits\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eOnce configured, we finally generate the keys. I usually recommend a shelf live of a year for keys. I think of security as a conscious effort, and this forces us to reconsider this again in the future.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egpg/card\u0026gt; generate\nMake off-card backup of encryption key? (Y/n) n\n\nPlease specify how long the key should be valid.\n         0 = key does not expire\n      \u0026lt;n\u0026gt;  = key expires in n days\n      \u0026lt;n\u0026gt;w = key expires in n weeks\n      \u0026lt;n\u0026gt;m = key expires in n months\n      \u0026lt;n\u0026gt;y = key expires in n years\nKey is valid for? (0) 1y\nKey expires at Fri 26 June 2021 11:20:37 PM EDT\nIs this correct? (y/N) y\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFinally, add some metadata about the key.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eGnuPG needs to construct a user ID to identify your key.\n\nReal name: Brandon Rozek\nEmail address: brandon@therozek.com\nComment: \nYou selected this USER-ID:\n    \u0026#34;Brandon Rozek \u0026lt;brandon@therozek.com\u0026gt;\u0026#34;\n\nChange (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAnd we\u0026rsquo;re done! Now you have three keys on your smartcard for signing, encrypting, and authenticating.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egpg/card\u0026gt; quit\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2020.06.05",
    "tags": ["GPG/PGP"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/sshconnectionsharing/",
    "url": "https://brandonrozek.com/blog/sshconnectionsharing/",
    "title": "SSH Connection Sharing",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIf you\u0026rsquo;re like me, you open a lot of different terminal sessions throughout your day. When it comes to SSH, I want these different sessions to share a connection rather than creating a new one each time.\u003c/p\u003e\n\u003cp\u003eTo accomplish this, I have the following in my \u003ccode\u003e~/.ssh/config\u003c/code\u003e file.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eControlMaster auto\nControlPersist no\nControlPath ~/.ssh/sockets/socket-%r@%h:%p\n\u003c/code\u003e\u003c/pre\u003e\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eOption\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eControlMaster\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eAllows connection sharing\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eControlPersist\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eyes\u003c/code\u003e to keep connection up even when no clients are connected. \u003ccode\u003e2s\u003c/code\u003e (or custom timeout) to keep the connection up for 2 seconds after no clients are connected.\u003ccode\u003eno\u003c/code\u003e to disconnect immediately\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eControlPath\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eWhere to store connection information. This should not be writable by other users.\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eYou\u0026rsquo;ll also need to create the \u003ccode\u003esockets\u003c/code\u003e folder if you don\u0026rsquo;t have it already setup.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir ~/.ssh/sockets\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echmod go-w ~/.ssh/sockets\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.06.05",
    "tags": ["SSH"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/dockermacvlan/",
    "url": "https://brandonrozek.com/blog/dockermacvlan/",
    "title": "Docker Macvlan Networks",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIt is useful to have some docker containers live in the same network as your host machine. We can accomplish this by creating a new MAC address for the container and using the \u003ccode\u003emacvlan\u003c/code\u003e driver. Here is example Docker Compose configuration\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yml\" data-lang=\"yml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eservices\u003c/span\u003e:  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003enginx\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eimage\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003elinuxserver/nginx\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003econtainer_name\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003enginx\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ehostname\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003enginx\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Randomized MAC address\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003emac_address\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e4E:64:A4:60:8D:0E\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eenvironment\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003ePUID\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003ePGID\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003evolumes\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003e/etc/nginx:/config/nginx\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003erestart\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ealways\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eports\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e/tcp\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003e443\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e/udp\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003enetworks\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003emacvlan_network\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# Static IP for host network\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eipv4_address\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e192.168.0.10\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#75715e\"\u003e# Allow access to nginx container in default docker network\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003edefault\u003c/span\u003e: \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003enetworks\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003emacvlan_network\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003edriver\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003emacvlan\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003edriver_opts\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#75715e\"\u003e# Obtain device name by looking at `ip addr`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003eparent\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eeno1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eipam\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003econfig\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        - \u003cspan style=\"color:#f92672\"\u003esubnet\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e192.168.0.0\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e/24\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.05.26",
    "tags": ["Containers"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/gitdropcommits/",
    "url": "https://brandonrozek.com/blog/gitdropcommits/",
    "title": "How to Drop Commits in Git",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eEven though it is not recommended to rewrite history in Git, it can be useful to drop certain commits from a pull request. The easiest way I\u0026rsquo;ve found to achieve this is with \u003ccode\u003egit rebase\u003c/code\u003e. To look back at the last 5 commits\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit rebase -i HEAD~5\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will produce a view like the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epick bda8e1d Follow better coding standards\npick ed62936 Bad Commit\npick 5b89e82 Refactoring to make more sense\npick 2941129 Delete Everything\npick 04d6558 New Feature\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eYou can then change the commits you want to remove from \u003ccode\u003epick\u003c/code\u003e to \u003ccode\u003edrop\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epick bda8e1d Follow better coding standards\ndrop ed62936 Bad Commit\npick 5b89e82 Refactoring to make more sense\ndrop 2941129 Delete Everything\npick 04d6558 New Feature\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eOnce you exit out, the two commits will be dropped.\u003c/p\u003e\n\u003cp\u003eInstead of analyzing the last 5 commits, you can also analyze the commits made after branching out. Let\u0026rsquo;s say we\u0026rsquo;re on a feature branch based on the \u003ccode\u003edevelopment\u003c/code\u003e branch.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit rebase -i development\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFrom there you would get the same \u003ccode\u003epick/drop\u003c/code\u003e screen as before.\u003c/p\u003e\n",
    "date_published": "2020.05.26",
    "tags": ["Git"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/obswebcam/",
    "url": "https://brandonrozek.com/blog/obswebcam/",
    "title": "Using OBS as a Webcam Source",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://obsproject.com/\"\u003eOpen Broadcaster Software\u003c/a\u003e is a great piece of software for recording and live streaming different video content. It would be nice to use the same level of professional software in video calling software as well.\u003c/p\u003e\n\u003cp\u003eNow there already exists a \u003ca href=\"https://github.com/CatxFish/obs-v4l2sink\"\u003eOBS plugin\u003c/a\u003e for this purpose, but it does require recompiling OBS with this plugin. This post will outline a possible technique to get around this, at a cost of performance.\u003c/p\u003e\n\u003cp\u003eTo see discussion around this topic, check out this \u003ca href=\"https://github.com/CatxFish/obs-virtual-cam/issues/17\"\u003eGithub thread\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eOBS has two different options, streaming and recording. Since I want the ability to record a high fidelity version of my feed, we will use streaming to push video to a local RTMP server. This local RTMP server will then re-encode the video to a \u003ca href=\"/blog/fakewebcam/\"\u003ev4l2 video device\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eWe will use \u003ccode\u003effmpeg\u003c/code\u003e as our RTMP server\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003effmpeg -hwaccel vdpau \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e       -fflags nobuffer \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e       -listen \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e       -i rtmp://localhost:12345/live/app/ \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e       -f v4l2 /dev/video20\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eFlag\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-hwaccel vdpau\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e(Optional) Allows \u003ccode\u003effmpeg\u003c/code\u003e to use GPU for acceleration\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-fflags nobuffer\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eTells \u003ccode\u003effmpeg\u003c/code\u003e to not bother buffering the video and work with the latest frame possible.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-listen 1\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eStarts a server to listen for video.\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eThen from OBS you will want to use the Auto-Configuration wizard to have it figure out the right set of parameters to use for the lowest latency. Once that is set you can hit the stream button and you are all set to use the fake video device for your calls! Depending on your hardware, you might notice a few seconds of latency.\u003c/p\u003e\n\u003cp\u003eTo quickly verify that you can see your OBS video from the video device.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003effplay -fflags nobuffer /dev/video20\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.05.25",
    "tags": ["Audio-Video"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/v4l2-webcam/",
    "url": "https://brandonrozek.com/blog/v4l2-webcam/",
    "title": "Loopback Webcam Device with V4l2",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn Linux you can create a fake webcam by making use of the \u003ccode\u003ev4l2loopback\u003c/code\u003e kernel module.\u003c/p\u003e\n\u003cp\u003eTo install,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install v4l2loopback-dkms\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you already had an existing module, remove it so we can customize it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo modprobe -r v4l2loopback\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen create the file \u003ccode\u003e/etc/modprobe.d/fakecam.conf\u003c/code\u003e and add the following line\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eoptions v4l2loopback devices=1 video_nr=20 card_label=fakecam exclusive_caps=1\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe value of \u003ccode\u003evideo_nr\u003c/code\u003e must be larger than the amount of cameras in your system. I wouldn\u0026rsquo;t have more than 10 webcams plugged into my system, so I set it to 20.\u003c/p\u003e\n\u003cp\u003eThen load back up the kernel module\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo modprobe v4l2loopback\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow when you open up Chrome, Skype, etc, you should see a new device labeled \u0026ldquo;fakecam\u0026rdquo;.\u003c/p\u003e\n",
    "date_published": "2020.05.25",
    "tags": ["Audio-Video"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/piholedhcpdns/",
    "url": "https://brandonrozek.com/blog/piholedhcpdns/",
    "title": "Configuring DHCP DNS in Pihole",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThere are two scenarios I can imagine in where you want to configure the DNS set by PiHole\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eYou have multiple PiHole\u0026rsquo;s in your LAN\u003c/li\u003e\n\u003cli\u003eYou have a multi-layer DNS setup\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eIn either case, we can utilize \u003ccode\u003ednsmasq\u003c/code\u003e configurations in order to set the DNS option.  You should already notice \u003ccode\u003e01-pihole.conf\u003c/code\u003e and \u003ccode\u003e02-pihole-dhcp.conf\u003c/code\u003e in \u003ccode\u003e/etc/dnsmasq.d\u003c/code\u003e, we\u0026rsquo;ll need to create one that is numbered \u003ccode\u003e03-*\u003c/code\u003e or greater. I\u0026rsquo;ll use \u003ccode\u003e/etc/dnsmasq.d/03-dhcp-dns.conf\u003c/code\u003e as my example.\u003c/p\u003e\n\u003cp\u003eTo set the initial DNS server to be from the router.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edhcp-option=6,192.168.0.1\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTo set multiple DNS servers\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edhcp-option=6,192.168.0.2,192.168.0.3\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2020.05.25",
    "tags": ["Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/wildcarddomainspihole/",
    "url": "https://brandonrozek.com/blog/wildcarddomainspihole/",
    "title": "Wildcard Domains in PiHole",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eAs of this time of writing, the current version of PiHole (5.0) supports adding custom DNS records, but not wildcard records. This makes it annoying if you run a bunch of different services within your LAN following a certain pattern.\u003c/p\u003e\n\u003cp\u003eThough since PiHole runs on top of \u003ccode\u003ednsmasq\u003c/code\u003e it is easy to add an additional configuration file to point a domain containing \u003ccode\u003eexample.com\u003c/code\u003e to a specific IP.\u003c/p\u003e\n\u003cp\u003eIf you look in \u003ccode\u003e/etc/dnsmasq.d/\u003c/code\u003e there are the files \u003ccode\u003e01-pihole.conf\u003c/code\u003e and \u003ccode\u003e02-pihole-dhcp.conf\u003c/code\u003e. For our wildcard record, we\u0026rsquo;re going to add a new file \u003ccode\u003e03-custom-dns.conf\u003c/code\u003e. Let\u0026rsquo;s have an example where we want to map \u003ccode\u003eexample.com\u003c/code\u003e and \u003ccode\u003e*.example.com\u003c/code\u003e to \u003ccode\u003e192.168.0.10\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eaddress=/example.com/192.168.0.10\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eOnce you save, restart the container or box and you will then have the wildcard record!\u003c/p\u003e\n",
    "date_published": "2020.05.25",
    "tags": ["Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/bashtcpudp/",
    "url": "https://brandonrozek.com/blog/bashtcpudp/",
    "title": "TCP/UDP with Bash",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe bash shell contains pseudo-devices to send packets with TCP/UDP. The pseudo files are formatted like the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edev/tcp/HOST/PORT\n/dev/udp/HOST/PORT\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTo send a UDP packet to \u003ccode\u003elocalhost:6300\u003c/code\u003e containing the payload \u0026ldquo;Hello, World.\u0026rdquo;, we would run the following command:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Hello, World.\u0026#34;\u003c/span\u003e \u0026gt; /dev/udp/127.0.0.1/6300\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.05.25",
    "tags": ["Bash","Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/gitcredentialstore/",
    "url": "https://brandonrozek.com/blog/gitcredentialstore/",
    "title": "Git Credential Store",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eNormally it is recommended to setup a SSH key with your Git hosting tool with choice. Though if that is not practical, another way you can avoid having to put in your git username and password every time you push your code is to make use of Git\u0026rsquo;s credential store.\u003c/p\u003e\n\u003cp\u003eTo enable it globally across your projects\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit config --global credential.helper store\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen you can edit the file \u003ccode\u003e~/.git-credentials\u003c/code\u003e with the following format\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ehttps://username1:password1@site1.com\nhttps://username2:password2@site2.com\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIt is recommended to set your \u003ccode\u003e.git-credentials\u003c/code\u003e file with the same permissions you would see in a SSH private key file. Read and write permissions for the user only.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echmod \u003cspan style=\"color:#ae81ff\"\u003e600\u003c/span\u003e ~/.git-credentials\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.05.25",
    "tags": ["Git"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/algorithmiclatex/",
    "url": "https://brandonrozek.com/blog/algorithmiclatex/",
    "title": "Algorithms in LaTex",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThere\u0026rsquo;s a great package in LaTex called \u003ca href=\"https://ctan.org/pkg/algorithms?lang=en\"\u003e\u003ccode\u003ealgorithm\u003c/code\u003e\u003c/a\u003e to help format psuedo-code algorithms for scientific papers. Here\u0026rsquo;s a simple example of its usage:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e\\usepackage{algorithm}\n\\usepackage[noend]{algpseudocode}\n\\begin{algorithm}\n\\caption{Even Numbers}\n\\begin{algorithmic}[1]\n\\State Set variable $evens$ to an empty list.\n\\For {every integer $i$}\n  \\If {$i$ is divisible by $2$}\n    \\State Add $i$ to $events$ list.\n  \\EndIf\n\\EndFor\n\\end{algorithmic}\n\\end{algorithm}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/20200514225618784.png\" alt=\"image-20200514225618784\"\u003e\u003c/p\u003e\n",
    "date_published": "2020.05.14",
    "tags": ["LaTex"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/appsusinginternet/",
    "url": "https://brandonrozek.com/blog/appsusinginternet/",
    "title": "Show Applications using the Internet",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThere\u0026rsquo;s a great thread on \u003ca href=\"https://askubuntu.com/questions/104739/which-applications-are-using-internet\"\u003eask ubuntu\u003c/a\u003e on seeing which applications are using the Internet. I\u0026rsquo;m going to add my own spin on the answers for future reference.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elsof -i | awk \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;{print $1}\u0026#39;\u003c/span\u003e | uniq | tail -n +2\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBreaking it down\u0026hellip;\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003elsof -i\u003c/code\u003e shows us a lot of information pertaining to processes accessing the Internet.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eawk '{print $1}'\u003c/code\u003e filters the last output to only shown us the \u003ccode\u003eCOMMAND\u003c/code\u003e column.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003euniq\u003c/code\u003e filters out multiple occurrences of a single application.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003etail -n +2\u003c/code\u003e removes the first \u003ccode\u003eCOMMAND\u003c/code\u003e line.\u003c/li\u003e\n\u003c/ul\u003e\n",
    "date_published": "2020.05.09",
    "tags": ["Linux","Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/gitlineendings/",
    "url": "https://brandonrozek.com/blog/gitlineendings/",
    "title": "Git Line Endings",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eif you have worked with a team that has a mix of Windows and Linux developers, you might have noticed pull requests where Git reports changes in a file that is not visible. One explanation is that the line endings might have changed.\u003c/p\u003e\n\u003cp\u003eDue to historical reasons Windows uses a line ending called CRLF which stands for carriage return + line feed. This dates back to type writers where once you hit the line feed button to push the paper up, you need to press the carriage return button to move back to the left side of the paper. \u003ca href=\"https://en.wikipedia.org/wiki/Carriage_return\"\u003eWikipedia\u003c/a\u003e has more of a history if you want to learn more.\u003c/p\u003e\n\u003cp\u003eMeanwhile, Linux only uses LF or line feed for its ending.  We can configure a repository with a \u003ccode\u003e.gitattributes\u003c/code\u003e file to automatically convert CRLF to LF line endings in order not to see these frivolous changes in our repository.\u003c/p\u003e\n\u003cp\u003eThe \u003ccode\u003e.gitattributes\u003c/code\u003e file looks like this\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e# Automatically convert CRLF-\u0026gt;LF on files git thinks are text\n* text=auto\n\n# Explicitly declare what are text files\n*.py text\n*.js text\n\n# Denote files that are binary and should not be modified.\n*.png binary\n*.jpg binary\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eMake sure that you don\u0026rsquo;t have any working changes that need to be committed as the following commands will convert all the line endings for text files in your repository to LF.\u003c/p\u003e\n\u003cp\u003eFirst let us commit the git attributes file\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit add .gitattributes\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit commit -m \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Added gitattributes file\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen let us convert all the line endings to LF\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit add --renormalize .\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit commit -m \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Convert line endings to LF\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen tell your coworkers to expect a large commit which only line ending changes\u0026hellip;.\u003c/p\u003e\n",
    "date_published": "2020.05.09",
    "tags": ["Git"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/autodeploydockerapps/",
    "url": "https://brandonrozek.com/blog/autodeploydockerapps/",
    "title": "Auto-Deploy Docker Applications",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis post will combine that last three posts on \u003ca href=\"/blog/snapshotswithpacker/\"\u003ePacker\u003c/a\u003e, \u003ca href=\"/blog/autodeployterraform/\"\u003eTerraform\u003c/a\u003e, and their \u003ca href=\"/blog/sharedpackerterraformconfig/\"\u003econfiguration\u003c/a\u003e to show an entire example of how to deploy a docker-compose application. We will specifically look at deploying a game called \u003ca href=\"https://www.minetest.net/\"\u003e\u003ccode\u003eminetest\u003c/code\u003e\u003c/a\u003e on DigitalOcean, but these principles can be adjusted to deploy your application as well. The entire setup is \u003ca href=\"https://github.com/Brandon-Rozek/minetest-deploy\"\u003edocumented on Github\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"shared-config\"\u003eShared Config\u003c/h2\u003e\n\u003cp\u003eWe\u0026rsquo;re going to use a shared configuration between Packer and Terraform. The template goes like this:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ebase_system_image = \u0026#34;ubuntu-20-04-x64\u0026#34;\nregion = \u0026#34;nyc3\u0026#34;\nsize = \u0026#34;512mb\u0026#34;\ndomain = \u0026#34;brandonrozek.com\u0026#34; # Replace\nsubdomain = \u0026#34;minetest\u0026#34;\n\n# Secrets\ndo_token = \u0026#34;DO-TOKEN-HERE\u0026#34; # Replace\nkey_name = \u0026#34;SSH-NAME-ON-DO-HERE\u0026#34; # Replace\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe\u0026rsquo;ll also need to define the types of these variables in \u003ccode\u003evariables.hcl\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003evariable \u0026#34;do_token\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;base_system_image\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;domain\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;key_name\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;subdomain\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;region\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;size\u0026#34; {\n  type = string\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"packer\"\u003ePacker\u003c/h2\u003e\n\u003cp\u003eCreate a packer directory and setup some symbolic links to the share configuration\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir packer \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e cd packer\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eln -s ../config variables.auto.pkrvars.hcl\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eln -s ../variables.hcl variables.pkr.hcl\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow let\u0026rsquo;s create a script named \u003ccode\u003esetup.sh\u003c/code\u003e that will run on top of our base image. This will install Docker and setup the firewall to allow SSH and Minetest traffic through.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003eapt update\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapt upgrade -y\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapt install -y docker.io docker-compose\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemctl enable docker-compose\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemctl start docker-compose\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eufw allow OpenSSH\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Add any firewall rules you need\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# for your application here\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eufw allow 30000/udp\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eufw --force enable\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe image that we\u0026rsquo;ll use for Minetest comes from \u003ca href=\"https://fleet.linuxserver.io/image?name=linuxserver/minetest\"\u003elinuxserverio\u003c/a\u003e. To configure docker-compose we\u0026rsquo;ll need a file named \u003ccode\u003edocker-compose.yml\u003c/code\u003e. Its contents will be highly similar to what is listed on their \u003ca href=\"https://github.com/linuxserver/docker-minetest/blob/466cde1f2fd38278fe69d33ea3b2f42df50e6b16/README.md\"\u003eGithub\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yml\" data-lang=\"yml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eversion\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2.1\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eservices\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eminetest\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eimage\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003elinuxserver/minetest\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003econtainer_name\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eminetest\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eenvironment\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003ePUID=1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003ePGID=1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003eTZ=US/Eastern\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003evolumes\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003e/volumes/minetest/config/.minetest:/config/.minetest\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eports\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003e30000\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e30000\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e/udp\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003erestart\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eunless-stopped\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe\u0026rsquo;ll need to create a \u003ccode\u003esystemd\u003c/code\u003e script called \u003ccode\u003edocker-compose.service\u003c/code\u003e for systemd to \u003ca href=\"https://brandonrozek.com/blog/composesystemd/\"\u003eenable docker-compose on startup\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eDocker Compose Application Service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRequires\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edocker.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAfter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003edocker.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eoneshot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRemainAfterExit\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eyes\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWorkingDirectory\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/root\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/docker-compose up -d\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStop\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/docker-compose down\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eTimeoutStartSec\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFinally we\u0026rsquo;ll need to write a packer configuration file \u003ccode\u003edo.pkr.hcl\u003c/code\u003e to create our snapshot.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esource \u0026#34;digitalocean\u0026#34; \u0026#34;web\u0026#34; {\n  api_token = var.do_token\n  image = var.base_system_image\n  region = var.region\n  size = var.size\n  ssh_username = \u0026#34;root\u0026#34;\n  snapshot_name = \u0026#34;packer-docker\u0026#34;\n}\n\n\nbuild {\n  sources = [\n    \u0026#34;source.digitalocean.web\u0026#34;,\n  ]\n\n  provisioner \u0026#34;file\u0026#34; {\n    source = \u0026#34;docker-compose.yml\u0026#34;\n    destination = \u0026#34;/root/docker-compose.yml\u0026#34;\n  }\n  \n  provisioner \u0026#34;file\u0026#34; {\n    source = \u0026#34;docker-compose.service\u0026#34;\n    destination = \u0026#34;/etc/systemd/system/docker-compose.service\u0026#34;\n  }\n  \n  provisioner \u0026#34;shell\u0026#34; {\n    scripts = [ \u0026#34;setup.sh\u0026#34; ]\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTo build our image we need to run \u003ccode\u003epacker build .\u003c/code\u003e in the directory with all these files.\u003c/p\u003e\n\u003ch2 id=\"terraform\"\u003eTerraform\u003c/h2\u003e\n\u003cp\u003eLike before, we need to tell terraform where to look for its configuration\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir terraform \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e cd terraform\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eln -s ../config terraform.tfvars\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eln -s ../variables.hcl variables.tf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo deploy, we only need to create one additional file that will tell digital ocean to create a droplet and assign a subdomain to that droplet.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eprovider \u0026#34;digitalocean\u0026#34; {\n    token = var.do_token\n}\n\ndata \u0026#34;digitalocean_ssh_key\u0026#34; laptop {\n    name = var.key_name\n}\n\ndata \u0026#34;digitalocean_droplet_snapshot\u0026#34; \u0026#34;packer_snapshot\u0026#34; {\n    name = \u0026#34;packer-docker\u0026#34;\n    most_recent = true\n}\n\n# Create a droplet\nresource \u0026#34;digitalocean_droplet\u0026#34; \u0026#34;web\u0026#34; {\n    name = \u0026#34;tf-1\u0026#34;\n    image = data.digitalocean_droplet_snapshot.packer_snapshot.id\n    region = var.region\n    size = var.size\n    ssh_keys = [data.digitalocean_ssh_key.laptop.id]\n    backups = false\n}\n\n# Attach a subdomain\nresource \u0026#34;digitalocean_record\u0026#34; \u0026#34;www\u0026#34; {\n  domain = var.domain\n  type   = \u0026#34;A\u0026#34;\n  name   = var.subdomain\n  value  = digitalocean_droplet.web.ipv4_address\n}\n\noutput \u0026#34;ip\u0026#34; {\n    value = digitalocean_droplet.web.ipv4_address\n}\n\noutput \u0026#34;domain\u0026#34; {\n    value = \u0026#34;${digitalocean_record.www.name}.${digitalocean_record.www.domain}\u0026#34;\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTo deploy run\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eterraform apply\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo later take down the minetest server, run\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eterraform destroy\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eThis method can be easily configured to run whichever docker services you\u0026rsquo;d like. All you have to do is edit the \u003ccode\u003epacker/docker.compose.yml\u003c/code\u003e file and \u003ccode\u003epacker/setup.sh\u003c/code\u003e to setup the firewall rules.\u003c/p\u003e\n",
    "date_published": "2020.05.09",
    "tags": ["Containers"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/sharedpackerterraformconfig/",
    "url": "https://brandonrozek.com/blog/sharedpackerterraformconfig/",
    "title": "Shared Packer \u0026 Terraform Config",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eYou might have noticed from my last two posts on \u003ca href=\"/blog/snapshotswithpacker/\"\u003ePacker\u003c/a\u003e and \u003ca href=\"/blog/autodeployterraform/\"\u003eTerraform\u003c/a\u003e that the configuration files are highly similar. In fact, we can trick them into sharing a configuration file!\u003c/p\u003e\n\u003ch2 id=\"shared-configuration\"\u003eShared Configuration\u003c/h2\u003e\n\u003cp\u003eFirst let\u0026rsquo;s create a file we\u0026rsquo;ll call \u003ccode\u003econfig\u003c/code\u003e that contains all our assignments. Here is an example configuration:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ebase_system_image = \u0026#34;ubuntu-20-04-x64\u0026#34;\nregion = \u0026#34;nyc3\u0026#34;\nsize = \u0026#34;512mb\u0026#34;\ndomain = \u0026#34;example.com\u0026#34;\nsubdomain = \u0026#34;temp\u0026#34;\n\n# Secrets\ndo_token = \u0026#34;DO-TOKEN-HERE\u0026#34;\nkey_name = \u0026#34;KEY-NAME-ON-DO\u0026#34;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThen we\u0026rsquo;ll create a file named \u003ccode\u003evariables.hcl\u003c/code\u003e that contains the type definitions\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003evariable \u0026#34;do_token\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;base_system_image\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;domain\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;key_name\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;subdomain\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;region\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;size\u0026#34; {\n  type = string\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"packer\"\u003ePacker\u003c/h2\u003e\n\u003cp\u003eNow to trick Packer into reading the configuration files we need to:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003emap \u003ccode\u003evariables.auto.pkrvars.hcl\u003c/code\u003e to \u003ccode\u003econfig\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003emap \u003ccode\u003evariables.pkr.hcl\u003c/code\u003e to \u003ccode\u003evariables.hcl\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWe can do this with symbolic links\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eln -s config variables.auto.pkrvars.hcl\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eln -s variables.hcl variables.pkr.hcl\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"terraform\"\u003eTerraform\u003c/h2\u003e\n\u003cp\u003eTo trick Terraform into reading the configuration files we need to:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003emap \u003ccode\u003eterraform.tfvars \u003c/code\u003e to \u003ccode\u003econfig\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003emap \u003ccode\u003evariables.tf\u003c/code\u003e to \u003ccode\u003evariables.hcl\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eAs before, we can do this with symbolic links\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eln -s config terraform.tfvars\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eln -s variables.hcl variables.tf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.05.08",
    "tags": ["Deployment"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/autodeployterraform/",
    "url": "https://brandonrozek.com/blog/autodeployterraform/",
    "title": "Automatic Deployments with Terraform",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI have recently written about \u003ca href=\"/blog/snapshotswithpacker/\"\u003ePacker\u003c/a\u003e to create system images or snapshots. This post will go over another \u003ca href=\"https://www.hashicorp.com/\"\u003eHashiCorp\u003c/a\u003e project named \u003ca href=\"https://www.terraform.io/\"\u003eTerraform\u003c/a\u003e that we can use to deploy that image to a VPS. Like before, I am going to go over how to setup this up in DigitalOcean. Check out \u003ca href=\"https://www.terraform.io/docs/providers/index.html\"\u003ethis list\u003c/a\u003e for documentation on your favorite cloud provider.\u003c/p\u003e\n\u003ch2 id=\"variables\"\u003eVariables\u003c/h2\u003e\n\u003cp\u003eTo protect against committing secrets like API keys, we\u0026rsquo;re going to create a file that only stores variables. For it to be loaded automatically, it needs to be named \u003ccode\u003eterraform.tfvars\u003c/code\u003e. Here is an example configuration:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eregion = \u0026#34;nyc3\u0026#34;\nsize = \u0026#34;512mb\u0026#34;\ndomain = \u0026#34;example.com\u0026#34;\nsubdomain = \u0026#34;temp\u0026#34;\n\n# Secrets\ndo_token = \u0026#34;DO-TOKEN-HERE\u0026#34;\nkey_name = \u0026#34;SSH-KEY-NAME-ON-DO\u0026#34;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow to define the variables in HCL, we need to create a separate \u003ccode\u003evariables.tf\u003c/code\u003e file defining their types\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003evariable \u0026#34;do_token\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;domain\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;key_name\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;subdomain\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;region\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;size\u0026#34; {\n  type = string\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"configuration\"\u003eConfiguration\u003c/h2\u003e\n\u003cp\u003eNow let\u0026rsquo;s create a file called \u003ccode\u003edo.tf\u003c/code\u003e.  We need to start off by stating which provider we are using\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eprovider \u0026#34;digitalocean\u0026#34; {\n    token = var.do_token\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIf you want to hook up your SSH key, then we need to query the Digital Ocean API for its ID.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edata \u0026#34;digitalocean_ssh_key\u0026#34; laptop {\n    name = var.key_name\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWe need to also query the API for the packer snapshot we created. Replace this with any standard image like \u003ccode\u003e\u0026quot;ubuntu-20-04-x64\u0026quot;\u003c/code\u003e if you don\u0026rsquo;t want to use a snapshot.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edata \u0026#34;digitalocean_droplet_snapshot\u0026#34; \u0026#34;packer_snapshot\u0026#34; {\n    name = \u0026#34;packer-example\u0026#34;\n    most_recent = true\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow we can create the droplet\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eresource \u0026#34;digitalocean_droplet\u0026#34; \u0026#34;web\u0026#34; {\n    name = \u0026#34;tf-1\u0026#34;\n    image = data.digitalocean_droplet_snapshot.packer_snapshot.id\n    region = var.region\n    size = var.size\n    ssh_keys = [data.digitalocean_ssh_key.laptop.id]\n    backups = false\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAttach a domain to the droplet\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eresource \u0026#34;digitalocean_record\u0026#34; \u0026#34;www\u0026#34; {\n  domain = var.domain\n  type   = \u0026#34;A\u0026#34;\n  name   = var.subdomain\n  value  = digitalocean_droplet.web.ipv4_address\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eOutput useful pieces of information like the new system\u0026rsquo;s IP address and the domain\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eoutput \u0026#34;ip\u0026#34; {\n    value = digitalocean_droplet.web.ipv4_address\n}\n\noutput \u0026#34;domain\u0026#34; {\n    value = \u0026#34;${digitalocean_record.www.name}.${digitalocean_record.www.domain}\u0026#34;\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe whole configuration file for your convenience:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eprovider \u0026#34;digitalocean\u0026#34; {\n    token = var.do_token\n}\n\ndata \u0026#34;digitalocean_ssh_key\u0026#34; laptop {\n    name = var.key_name\n}\n\ndata \u0026#34;digitalocean_droplet_snapshot\u0026#34; \u0026#34;packer_snapshot\u0026#34; {\n    name = \u0026#34;packer-example\u0026#34;\n    most_recent = true\n}\n\n# Create a droplet\nresource \u0026#34;digitalocean_droplet\u0026#34; \u0026#34;web\u0026#34; {\n    name = \u0026#34;tf-1\u0026#34;\n    image = data.digitalocean_droplet_snapshot.packer_snapshot.id\n    region = var.region\n    size = var.size\n    ssh_keys = [data.digitalocean_ssh_key.laptop.id]\n    backups = false\n}\n\n# Attach a subdomain\nresource \u0026#34;digitalocean_record\u0026#34; \u0026#34;www\u0026#34; {\n  domain = var.domain\n  type   = \u0026#34;A\u0026#34;\n  name   = var.subdomain\n  value  = digitalocean_droplet.web.ipv4_address\n}\n\noutput \u0026#34;ip\u0026#34; {\n    value = digitalocean_droplet.web.ipv4_address\n}\n\noutput \u0026#34;domain\u0026#34; {\n    value = \u0026#34;${digitalocean_record.www.name}.${digitalocean_record.www.domain}\u0026#34;\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"deploy\"\u003eDeploy\u003c/h1\u003e\n\u003cp\u003eCheck if your configuration is valid\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eterraform plan\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDeploy!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eterraform apply\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTake down when done\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eterraform destroy\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.05.08",
    "tags": ["Deployment"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/snapshotswithpacker/",
    "url": "https://brandonrozek.com/blog/snapshotswithpacker/",
    "title": "Snapshot Creation with Packer",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://www.packer.io/\"\u003ePacker\u003c/a\u003e is a tool to create automated machine images in both local virtual machine / container environments, as well as a variety of cloud platforms. My current cloud platform of choice is \u003ca href=\"https://www.digitalocean.com/\"\u003eDigital Ocean\u003c/a\u003e, so this post will explain how to set up Packer with it. Though you can likely find your platform of choice on their \u003ca href=\"https://www.packer.io/docs/builders/\"\u003edocs\u003c/a\u003e page\u003c/p\u003e\n\u003cp\u003eIn this post I am going to use the beta configuration language of HCL2. This requires a Packer version of at least 1.5.\u003c/p\u003e\n\u003ch2 id=\"variables\"\u003eVariables\u003c/h2\u003e\n\u003cp\u003eFirst let us set up a variables file which we will later reference. This makes it easy to keep your main Packer configuration files in Git, while not committing your API key.\u003c/p\u003e\n\u003cp\u003eCreate a file called \u003ccode\u003evariables.auto.pkrvars.hcl\u003c/code\u003e. Here is some example variables and values that I put in mine.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ebase_system_image = \u0026#34;ubuntu-20-04-x64\u0026#34;\nregion = \u0026#34;nyc3\u0026#34;\nsize = \u0026#34;512mb\u0026#34;\ndo_token = \u0026#34;DOTOKENHERE\u0026#34; # Secret\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThen we need to create \u003ccode\u003evariables.pkr.hcl\u003c/code\u003e that define the types of each of these variables\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003evariable \u0026#34;do_token\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;base_system_image\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;region\u0026#34; {\n  type = string\n}\n\nvariable \u0026#34;size\u0026#34; {\n  type = string\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"provisioning\"\u003eProvisioning\u003c/h2\u003e\n\u003cp\u003eOnce the system is up and running we can use a \u003ca href=\"https://www.packer.io/docs/provisioners/\"\u003evariety of tools\u003c/a\u003e that setup the image.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eAnsible\u003c/li\u003e\n\u003cli\u003eChef\u003c/li\u003e\n\u003cli\u003ePowershell\u003c/li\u003e\n\u003cli\u003eetc.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eI\u0026rsquo;ll use a simple bash script as an example\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003eapt update\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapt upgrade -y\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"piecing-it-together\"\u003ePiecing it together\u003c/h2\u003e\n\u003cp\u003eFinally let us create a \u003ccode\u003edo.pkr.hcl\u003c/code\u003e file that contains the following information\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esource \u0026#34;digitalocean\u0026#34; \u0026#34;web\u0026#34; {\n  api_token = var.do_token\n  image = var.base_system_image\n  region = var.region\n  size = var.size\n  ssh_username = \u0026#34;root\u0026#34;\n  snapshot_name = \u0026#34;packer-example\u0026#34;\n}\n\n\nbuild {\n  sources = [\n    \u0026#34;source.digitalocean.web\u0026#34;,\n  ]\n\n  provisioner \u0026#34;shell\u0026#34; {\n    scripts = [ \u0026#34;setup.sh\u0026#34; ]\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAssuming all the files we\u0026rsquo;ve just created are in the same directory, Packer will automatically recognize where the value for \u003ccode\u003evar.do_token\u003c/code\u003e lives. We can then run packer to build the image\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epacker build .\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis sets up a snapshot called \u003ccode\u003epacker-example\u003c/code\u003e which we can later spin up and use! Keep in mind that it does a small amount of money to store images on DigitalOcean.\u003c/p\u003e\n",
    "date_published": "2020.05.08",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/doapi/",
    "url": "https://brandonrozek.com/blog/doapi/",
    "title": "Digital Ocean API",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis post is meant for the times that you want to quickly query the Digital Ocean API v2, but do not want to download their great client \u003ca href=\"https://github.com/digitalocean/doctl\"\u003e\u003ccode\u003edoctl\u003c/code\u003e\u003c/a\u003e. The prerequisite for this post is that you have a \u003ca href=\"https://www.digitalocean.com/docs/apis-clis/api/create-personal-access-token/\"\u003epersonal access token\u003c/a\u003e configured inside the \u003ccode\u003e$DO_TOKEN\u003c/code\u003e environmental variable.\u003c/p\u003e\n\u003ch2 id=\"list-ssh-keys\"\u003eList SSH Keys\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl --silent \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --request GET \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --header \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Authorization: Bearer \u003c/span\u003e$DO_TOKEN\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     https://api.digitalocean.com/v2/account/keys\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you have \u003ca href=\"https://stedolan.github.io/jq/\"\u003e\u003ccode\u003ejq\u003c/code\u003e\u003c/a\u003e configured:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl --silent \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --request GET \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --header \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Authorization: Bearer \u003c/span\u003e$DO_TOKEN\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     https://api.digitalocean.com/v2/account/keys |\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ejq \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;.ssh_keys[] | \u0026#34;\\(.name): \\(.id)\u0026#34;\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"list-images\"\u003eList Images\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl --silent \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --request GET \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --header \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Authorization: Bearer \u003c/span\u003e$DO_TOKEN\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     https://api.digitalocean.com/v2/images?type\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003edistribution\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you have \u003ca href=\"https://stedolan.github.io/jq/\"\u003e\u003ccode\u003ejq\u003c/code\u003e\u003c/a\u003e configured:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl --silent \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --request GET \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --header \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Authorization: Bearer \u003c/span\u003e$DO_TOKEN\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     https://api.digitalocean.com/v2/images?type\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003edistribution |\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ejq \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;.images[] | .slug\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"list-regions\"\u003eList Regions\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl --silent \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --request GET \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --header \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Authorization: Bearer \u003c/span\u003e$DO_TOKEN\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     https://api.digitalocean.com/v2/regions\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you have \u003ca href=\"https://stedolan.github.io/jq/\"\u003e\u003ccode\u003ejq\u003c/code\u003e\u003c/a\u003e configured:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl --silent \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --request GET \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --header \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Authorization: Bearer \u003c/span\u003e$DO_TOKEN\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     https://api.digitalocean.com/v2/regions |\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ejq \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;.regions[] | \u0026#34;\\(.name): \\(.slug)\u0026#34;\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"list-snapshots\"\u003eList Snapshots\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl --silent \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --request GET \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --header \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Authorization: Bearer \u003c/span\u003e$DO_TOKEN\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     https://api.digitalocean.com/v2/snapshots\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you have \u003ca href=\"https://stedolan.github.io/jq/\"\u003e\u003ccode\u003ejq\u003c/code\u003e\u003c/a\u003e configured:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl --silent \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --request GET \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --header \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Authorization: Bearer \u003c/span\u003e$DO_TOKEN\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     https://api.digitalocean.com/v2/snapshots |\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ejq \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;.snapshots[] | .name\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"bash-script\"\u003eBash Script\u003c/h2\u003e\n\u003cp\u003eHere is a small bash script that combines the functionality above into one file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003eJQ_INSTALLED\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003ecommand -v jq \u0026gt; /dev/null\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e show_usage \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Usage: ./do-list (keys|images|regions|snapshots)\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e query_api \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    curl --silent \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e         --request GET \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e         --header \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Authorization: Bearer \u003c/span\u003e$DO_TOKEN\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e         https://api.digitalocean.com/v2/\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e list_keys \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    output\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003equery_api account/keys\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e $JQ_INSTALLED; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$output\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | jq \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;.ssh_keys[] | \u0026#34;\\(.name): \\(.id)\u0026#34;\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$output\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e list_images \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    output\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003equery_api images?type\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003edistribution\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e $JQ_INSTALLED; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$output\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | jq \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;.images[] | .slug\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$output\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e list_regions \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    output\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003equery_api regions\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e $JQ_INSTALLED; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$output\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | jq \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;.regions[] | \u0026#34;\\(.name): \\(.slug)\u0026#34;\u0026#39;\u003c/span\u003e            \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$output\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e list_snapshots \u003cspan style=\"color:#f92672\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    output\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003equery_api snapshots\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e $JQ_INSTALLED; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$output\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e | jq \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;.snapshots[] | .name\u0026#39;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$output\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e $1 in\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    keys\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        list_keys;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    images\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        list_images;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    regions\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        list_regions;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    snapshots\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        list_snapshots;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    *\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        echo Unknown Parameter \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#39;\u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        show_usage;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eesac\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.05.06",
    "tags": ["Deployment"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pythonall/",
    "url": "https://brandonrozek.com/blog/pythonall/",
    "title": "Quick Python: __all__",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eAnything that is defined inside a package that is imported is also brought into that workspace. So for example, if package \u003ccode\u003eA\u003c/code\u003e uses \u003ccode\u003enumpy\u003c/code\u003e and you import \u003ccode\u003eA\u003c/code\u003e, then \u003ccode\u003eA.numpy\u003c/code\u003e will be shown in your workspace. In order to limit the variables exported, you can define the ones you want to be shown in a variable called \u003ccode\u003e__all__\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eInside your package \u003ccode\u003etest.py\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehidden_variable \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eshown_variable \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e__all__ \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;shown_variable\u0026#39;\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen if you open the standard Python REPL and import \u003ccode\u003etest\u003c/code\u003e, only the variable \u003ccode\u003eshown_variable\u003c/code\u003e will be seen.\u003c/p\u003e\n",
    "date_published": "2020.05.01",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pyextradeps/",
    "url": "https://brandonrozek.com/blog/pyextradeps/",
    "title": "Python Packaging: Optional Dependencies",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIt is possible to make different package group of optional dependencies for a Python package. This is useful if you want to include an extra set of dependencies for developers/maintainers of the package. We can also define a plugin-based package similarly to how \u003ca href=\"https://github.com/openai/gym/blob/67212547ac29296839434324a0d5996e48fae840/setup.py\"\u003eOpenAI Gym uses it\u003c/a\u003e to denote different categories of environments you can setup.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e setuptools \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e setup, find_packages\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eextras \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;typing\u0026#39;\u003c/span\u003e: [\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;mypy~=0.740\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;mypy-extensions~=0.4.0\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;pylint~=2.4.4\u0026#39;\u003c/span\u003e],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;testingdocs\u0026#39;\u003c/span\u003e: [\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;tox~=3.14.6\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Sphinx~=3.0.1\u0026#39;\u003c/span\u003e],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Meta dependency groups.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eextras[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;all\u0026#39;\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [item \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e group \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e extras\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003evalues() \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e item \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e group]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esetup(name\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;example\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      version\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;0.0.1\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      packages\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003efind_packages(),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      install_requires\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Flask~=1.1.1\u0026#39;\u003c/span\u003e],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      extras_require\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eextras,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.05.01",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/diceware/",
    "url": "https://brandonrozek.com/blog/diceware/",
    "title": "Generate Secure Passphrases Locally with Diceware",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eDiceware is a passphrase generator proposed by \u003ca href=\"http://diceware.com/\"\u003eArnold G. Reinhold\u003c/a\u003e. Passphrases contain multiple words which are chosen according to a sequence of dice rolls. Let\u0026rsquo;s look at a simplified example where we have binary dice (0 or 1) and we have a wordlist of two dice rolls.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e00\tabacus\n01\tabdomen\n10\tabdominal\n11\tabide\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIf you roll a zero twice, then you choose the word \u003ccode\u003eabacus\u003c/code\u003e. If you roll a zero and then a one, you choose the word \u003ccode\u003eabdomen\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eIn reality, \u003ca href=\"https://www.eff.org/about/staff/joseph-bonneau\"\u003eJoseph Bonneau\u003c/a\u003e over at the EFF, compiled a wordlist that consists of six dice rolls with a regular 5 sided dice. Resulting in a total of $6^5$ or 7776 different \u003ca href=\"https://www.eff.org/files/2016/07/18/eff_large_wordlist.txt\"\u003eenglish words\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eInstead of rolling physical dice forever, we can use a nicely put together python package called \u003ca href=\"https://github.com/ulif/diceware/\"\u003e\u003ccode\u003ediceware\u003c/code\u003e\u003c/a\u003e. It is easily installable via pip: \u003ccode\u003epip install diceware\u003c/code\u003e. The README explains the security implications far better than I can. At the time of writing, it uses by default \u003ccode\u003eurandom\u003c/code\u003e on Linux to choose 6 words from the EFF word list from before.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ediceware\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eGave me the random passphrase \u0026ldquo;DrearilyUncorruptOutboardKneeSubzeroGumdrop\u0026rdquo;.\u003c/p\u003e\n",
    "date_published": "2020.05.01",
    "tags": ["Security"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/latexcode/",
    "url": "https://brandonrozek.com/blog/latexcode/",
    "title": "Code in LaTex",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI am currently working on a paper in LaTex and wanted to include some source code in it. I didn\u0026rsquo;t want to use the default \u003ccode\u003everbatim\u003c/code\u003e environment since I wanted to include syntax highlighting as well. Luckily, the \u003ccode\u003elistings\u003c/code\u003e package is an easy and extensible way to include source code inside LaTex documents. To speak first of its extensibility, here is a subset of the arguments that it can possibly take:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eArgument\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eaboveskip\u003c/td\u003e\n          \u003ctd\u003eAmount of space to include above code.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ebackgroundcolor\u003c/td\u003e\n          \u003ctd\u003eBackground color\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ebasicstyle\u003c/td\u003e\n          \u003ctd\u003eFont-style of code (Color \u0026amp; Size)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ebelowskip\u003c/td\u003e\n          \u003ctd\u003eAmount of space to include below code.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ebreakatwhitespace\u003c/td\u003e\n          \u003ctd\u003eOnly break at whitespace (boolean)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ebreaklines\u003c/td\u003e\n          \u003ctd\u003eAutomatic Line Breaking (boolean)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ecommentstyle\u003c/td\u003e\n          \u003ctd\u003eFont-style of comments (Color \u0026amp; Size)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eframe\u003c/td\u003e\n          \u003ctd\u003eType of frame: \u003ccode\u003el\u003c/code\u003e for left, \u003ccode\u003er\u003c/code\u003e for right, \u003ccode\u003et\u003c/code\u003e for top, \u003ccode\u003eb\u003c/code\u003e for bottom; can use combination of letters or \u003ccode\u003esingle\u003c/code\u003e.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ekeywordstyle\u003c/td\u003e\n          \u003ctd\u003eFont-style of keywords (Color \u0026amp; Size)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003elanguage\u003c/td\u003e\n          \u003ctd\u003eLanguage of code (for highlighting purposes)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003enumbers\u003c/td\u003e\n          \u003ctd\u003eWhere to put the numbers: \u003ccode\u003enone\u003c/code\u003e, \u003ccode\u003eleft\u003c/code\u003e, or \u003ccode\u003eright\u003c/code\u003e.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003enumbersep\u003c/td\u003e\n          \u003ctd\u003eHow far the line-numbers are from the code.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003enumberstyle\u003c/td\u003e\n          \u003ctd\u003eFont-style of numbers (Color \u0026amp; Size)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003estringstyle\u003c/td\u003e\n          \u003ctd\u003eFont-style of strings in code (Color \u0026amp; Size)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003etabsize\u003c/td\u003e\n          \u003ctd\u003eDefault tab size in terms of spaces.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003etitle\u003c/td\u003e\n          \u003ctd\u003eShow filename included in \u003ccode\u003e\\lstinputlistings\u003c/code\u003e or caption.\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eHere is an example of code that I used in a paper\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-latex\" data-lang=\"latex\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usepackage\u003c/span\u003e{color}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\usepackage\u003c/span\u003e{listings}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\definecolor\u003c/span\u003e{keyblue}{rgb}{0.1, 0.1, 0.6}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\definecolor\u003c/span\u003e{dkgreen}{rgb}{0,0.6,0}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\definecolor\u003c/span\u003e{gray}{rgb}{0.5,0.5,0.5}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\definecolor\u003c/span\u003e{stringcol}{rgb}{0.58,0.4,0.1}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\lstset\u003c/span\u003e{frame=l,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  language=Python,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  aboveskip=3mm,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  belowskip=3mm,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  columns=flexible,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  basicstyle={\u003cspan style=\"color:#66d9ef\"\u003e\\small\\ttfamily\u003c/span\u003e},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  numbers=left,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  numberstyle=\u003cspan style=\"color:#66d9ef\"\u003e\\tiny\\color\u003c/span\u003e{gray},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  numbersep=2mm,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  keywordstyle=\u003cspan style=\"color:#66d9ef\"\u003e\\color\u003c/span\u003e{keyblue},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  commentstyle=\u003cspan style=\"color:#66d9ef\"\u003e\\color\u003c/span\u003e{dkgreen},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  stringstyle=\u003cspan style=\"color:#66d9ef\"\u003e\\color\u003c/span\u003e{stringcol},\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  breaklines=true,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  tabsize=4\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\begin\u003c/span\u003e{lstlisting}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edef greeting():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\treturn \u0026#34;Hello!\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e# Printing the greeting to the screen\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(greeting())\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e\\end\u003c/span\u003e{lstlisting}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/20200501001231572.png\" alt=\"image-20200501001231572\"\u003e\u003c/p\u003e\n",
    "date_published": "2020.04.30",
    "tags": ["LaTex"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/openvpncontainer/",
    "url": "https://brandonrozek.com/blog/openvpncontainer/",
    "title": "OpenVPN Container",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eInstead of configuring multiple containers to use a VPN, we can setup a VPN container and route the other containers traffic through this container. This post will outline how to do that with \u003ca href=\"https://github.com/dperson/openvpn-client\"\u003edperson\u0026rsquo;s OpenVPN Container\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;m a huge fan of docker-compose, so here we go:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eversion\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;3.3\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eservices\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eopenvpn-client\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eimage\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003edperson/openvpn-client\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ecap_add\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003enet_admin\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003esecurity_opt\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003elabel:disable\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003econtainer_name\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eopenvpn-client\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ehostname\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eopenvpn-client\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eenvironment\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003ePUID=1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003ePGID=1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003evolumes\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003e/dev/net:/dev/net:z\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003e/volumes/openvpn-client/vpn/:/vpn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003erestart\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ealways\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003enet_admin\u003c/code\u003e capability according to the documentation \u0026ldquo;perform various network-related operations\u0026rdquo;.  This would make sense since an additional network interface is configured for a VPN connection. The \u003ccode\u003elabel:disable\u003c/code\u003e definition is to disable label confinement.\u003c/p\u003e\n\u003cp\u003eIn this setup, you will need to put the \u003ccode\u003e.ovpn\u003c/code\u003e profile that you wish to connect to under the \u003ccode\u003e/volumes/openvpn-client/vpn/\u003c/code\u003e directory.\u003c/p\u003e\n\u003ch2 id=\"optional-usernamepassword-setup\"\u003e(Optional) Username/Password Setup\u003c/h2\u003e\n\u003cp\u003eIn the event you need a username and password to connect, create a file called \u003ccode\u003epass.txt\u003c/code\u003e in the same directory as your ovpn profile. The file \u003ccode\u003epass.txt\u003c/code\u003e will contain the username in the first line and the password in the second line. Then in your ovpn profile make sure you have a line that says \u003ccode\u003eauth-user-pass pass.txt\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"routing-traffic-through-vpn\"\u003eRouting Traffic through VPN\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s say your ISP throttles torrent connections and you want to route your \u003ccode\u003eqBittorrent\u003c/code\u003e container so that you can download Linux distributions faster. Here\u0026rsquo;s how you can define it in the docker-compose file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eqbittorrent\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eimage\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003elinuxserver/qbittorrent\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003econtainer_name\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eqbittorrent\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eenvironment\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003ePUID=1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003ePGID=1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003eUMASK_SET=022\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003eWEBUI_PORT=8000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003evolumes\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003e/volumes/qbittorrent/config:/config\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003e/volumes/qbittorrent/downloads:/downloads\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003enetwork_mode\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eservice:openvpn-client\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003erestart\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ealways\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"network-workarounds\"\u003eNetwork Workarounds\u003c/h2\u003e\n\u003cp\u003eSadly as of the time of writing, routing a container\u0026rsquo;s traffic makes it lose its ability to belong to a network. I knocked into this when I tried accessing the qBittorrent API. So for the sake of example, if you want to connect to qbittorrent, you need to route the traffic to the openvpn-client container at port 8000 which we specified earlier to be the webui port of qbittorrent.\u003c/p\u003e\n",
    "date_published": "2020.04.30",
    "tags": ["Containers"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/checkinstall/",
    "url": "https://brandonrozek.com/blog/checkinstall/",
    "title": "Quick and Dirty DEB/RPM with Checkinstall",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eTo create a quick and dirty Debian or RPM package, check out \u003ccode\u003echeckinstall\u003c/code\u003e! Be forewarned though that this isn\u0026rsquo;t the recommended way of creating packages. This post on \u003ca href=\"https://askubuntu.com/questions/1138384/why-is-checkinstall-no-longer-being-maintained\"\u003eAskUbuntu\u003c/a\u003e gives good reasons for why. Though if it is between running a \u003ccode\u003emake install\u003c/code\u003e or running this utility, I would consider running \u003ccode\u003echeckinstall\u003c/code\u003e instead.\u003c/p\u003e\n\u003cp\u003eThis program works by tracking all the files installed by a \u003ccode\u003emake install\u003c/code\u003e equivalent. This makes it easy to remove later on.\u003c/p\u003e\n\u003cp\u003eTo install on a debian based distribution,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install checkinstall\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen you can go to the directory that you normally \u003ccode\u003emake install\u003c/code\u003e and instead run the following to make a Debian package.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo checkinstall -D --install\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eno --nodoc\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIt will ask you to fill in various metadata such as name and author, and then it will create a package you can install!\u003c/p\u003e\n\u003cp\u003eTo install,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dpkg -i filename.deb\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can later remove it with apt.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt remove package_name\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf the application does not use \u003ccode\u003emake install\u003c/code\u003e, then you can add extra arguments to denote its equivalent\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo checkinstall -D --install\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eno --nodoc ./customInstallScript\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eArguments to \u003ccode\u003echeckinstall\u003c/code\u003e\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eFlag\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-D\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eCreate a Debian package\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-R\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eCreate a RPM package\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-S\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eCreate a Slackware package\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--install=no\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eDon\u0026rsquo;t install package\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--nodoc\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eDo not include documentation filesq\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n",
    "date_published": "2020.04.26",
    "tags": ["Packaging","Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/audioreplace/",
    "url": "https://brandonrozek.com/blog/audioreplace/",
    "title": "Replace Audio in Video",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI recorded a video and wanted to touch up my audio in audacity. Here\u0026rsquo;s how I used \u003ccode\u003effmpeg\u003c/code\u003e to extract the audio, and then replace it with a modified version.\u003c/p\u003e\n\u003ch2 id=\"extract-audio\"\u003eExtract Audio\u003c/h2\u003e\n\u003cp\u003eIf you know the format of the audio (mp3, ogg, aac) then it\u0026rsquo;s possible to do a byte copy of the audio track into a file:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003effmpeg -i input_video.mkv -vn -acodec copy output.aac\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eArgument\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-i\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eInput\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-vn\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eNo Video\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-acodec copy\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eCopy audio stream directly\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eIf you don\u0026rsquo;t know the audio codec and have \u003ccode\u003emediainfo\u003c/code\u003e installed, then run\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emediainfo --Inform\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Audio;%Format%\u0026#34;\u003c/span\u003e input_video.mkv\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you gave up, then you can transcode the audio (will take longer than direct copy)\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003effmpeg -i input_video.mkv -vn output.aac\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"replacing-audio\"\u003eReplacing Audio\u003c/h2\u003e\n\u003cp\u003eOnce you\u0026rsquo;re done touching up the audio (\u003ccode\u003etouchup.mp3\u003c/code\u003e), you\u0026rsquo;ll want to replace the existing audio with it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003effmpeg -i input_video.mkv \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e       -i touchup.mp3 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e       -c:v copy \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e       -map 0:v:0 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e       -map 1:a:0 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e       output_video.mp4\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eArgument\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-i\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eInputs\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-c:v copy\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eMake this a copy operation\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-c:v copy -map 0:v:0\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eMap the video from the first input to the first video output\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e-map 1:a:0\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eMap the audio from the second input to the first video output\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n",
    "date_published": "2020.04.20",
    "tags": ["Audio-Video"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pycacheprop/",
    "url": "https://brandonrozek.com/blog/pycacheprop/",
    "title": "Quick Python: Cached Property",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIf you have a property in an object that only needs to be computed once, consider using \u003ccode\u003ecached_property\u003c/code\u003e to store the result and serve for future function calls.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e functools\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eNumber\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __init__(self, n):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003en \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e n\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003e@functools.cached_property\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eis_prime\u003c/span\u003e(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e all(self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003en \u003cspan style=\"color:#f92672\"\u003e%\u003c/span\u003e i \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e i \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e range(\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e, self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003en))\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLet\u0026rsquo;s test it with the Mersenne prime \u003ccode\u003e524287\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003en \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Number(\u003cspan style=\"color:#ae81ff\"\u003e524287\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003en\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eis_prime\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAfter maybe 1-2 seconds of thinking you should get \u003ccode\u003eTrue.\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eRun it again and the result will be instantaneous!\u003c/p\u003e\n",
    "date_published": "2020.04.18",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/quickpythonhttp/",
    "url": "https://brandonrozek.com/blog/quickpythonhttp/",
    "title": "Quick Python: HTTP Server",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eYou can use Python to quickly spin up a HTTP server. A common use case for me is to quickly transfer files to mobile devices in my internal network.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epython \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003em http\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eserver\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will likely start an HTTP server on port 8000 on your machine listening to all network interfaces.\u003c/p\u003e\n",
    "date_published": "2020.04.18",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/internalca/",
    "url": "https://brandonrozek.com/blog/internalca/",
    "title": "Quick CA for internal LAN",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eSetting up trusted HTTPs inside a network without exposure to the Internet requires creating a Certificate Authority. The audience for this post is oriented for people setting up services in a small low threat model environment. Additional cautions should be applied when setting this up for a business, for example working off an intermediate CA.\u003c/p\u003e\n\u003cp\u003eWe\u0026rsquo;re going to be using \u003ca href=\"https://blog.cloudflare.com/introducing-cfssl/\"\u003eCFSSL\u003c/a\u003e, this is Cloudflare\u0026rsquo;s PKI toolkit to accomplish this. To install on Ubuntu,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install golang-cfssl\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"creating-the-ca\"\u003eCreating the CA\u003c/h2\u003e\n\u003cp\u003eThis tool makes heavy use of JSON for its configuration. To setup a CA, first let\u0026rsquo;s create \u003ccode\u003ecsr_ca.json\u003c/code\u003e that contains the following information\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;CN\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Common Name\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;key\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;algo\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;rsa\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;size\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e2048\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;names\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e       {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e         \u003cspan style=\"color:#f92672\"\u003e\u0026#34;C\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;US\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e         \u003cspan style=\"color:#f92672\"\u003e\u0026#34;O\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Orgnaization\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e         \u003cspan style=\"color:#f92672\"\u003e\u0026#34;OU\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Organizational Unit\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e         \u003cspan style=\"color:#f92672\"\u003e\u0026#34;ST\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Washington\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e         \u003cspan style=\"color:#f92672\"\u003e\u0026#34;L\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Locality\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e       }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhere \u003ccode\u003eC\u003c/code\u003e is the two-letter country code and \u003ccode\u003eST\u003c/code\u003e is the full state name.\u003c/p\u003e\n\u003cp\u003eThen to create the certificate authority\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecfssl gencert -initca csr_ca.json | cfssljson -bare ca\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will create the following files\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eFilename\u003c/th\u003e\n          \u003cth\u003ePurpose\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eca.pem\u003c/td\u003e\n          \u003ctd\u003ePublic Certificate\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eca-key.pem\u003c/td\u003e\n          \u003ctd\u003ePrivate Key\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eca.csr\u003c/td\u003e\n          \u003ctd\u003eCertificate Signing Request\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"creating-certficates\"\u003eCreating Certficates\u003c/h2\u003e\n\u003cp\u003eNow we can create SSL certificates for whatever websites we wish by specifying in a file we\u0026rsquo;ll call \u003ccode\u003ecsr_client.json\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;hosts\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;example.com\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;*.example.com\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;key\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;algo\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;rsa\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;size\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e2048\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;names\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e     {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e       \u003cspan style=\"color:#f92672\"\u003e\u0026#34;C\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;US\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e       \u003cspan style=\"color:#f92672\"\u003e\u0026#34;O\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Orgnaization\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e       \u003cspan style=\"color:#f92672\"\u003e\u0026#34;OU\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Organizational Unit\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e       \u003cspan style=\"color:#f92672\"\u003e\u0026#34;ST\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Washington\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e       \u003cspan style=\"color:#f92672\"\u003e\u0026#34;L\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Locality\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e     }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen to create the certs,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecfssl gencert -ca\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eca.pem -ca-key\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eca-key.pem csr_client.json | cfssljson -bare cert\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIt will create the private key, public certificate, and CSR just like the previous command. By default the certificate will last for one year and has the following usages:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eSigning\u003c/li\u003e\n\u003cli\u003eKey Encipherment\u003c/li\u003e\n\u003cli\u003eServer Authentication\u003c/li\u003e\n\u003cli\u003eClient Authentication\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eTo have more full grained control over the certificate usages and expiry time, I will defer you to the documentation. It involves creating another JSON file to pass as a flag into \u003ccode\u003ecfssl gencert\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"trusting-the-ca\"\u003eTrusting the CA\u003c/h2\u003e\n\u003cp\u003eOn Linux, I know of two different ways to trust the CA depending on your distribution.\u003c/p\u003e\n\u003ch3 id=\"ubuntu-derivative\"\u003eUbuntu Derivative\u003c/h3\u003e\n\u003cp\u003eFirst you need to copy  the \u003ccode\u003eca.pem\u003c/code\u003e file over to \u003ccode\u003e/usr/local/share/ca-certificates/\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mv ca.pem /usr/local/share/ca-certificates\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen you need to execute\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo update-ca-certificates\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"fedora-derivative\"\u003eFedora Derivative\u003c/h3\u003e\n\u003cp\u003eCopy \u003ccode\u003eca.pem\u003c/code\u003e over to \u003ccode\u003e/etc/pki/ca-trust/source/anchors\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mv ca.pem /etc/pki/ca-trust/source/anchors\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen execute\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo update-ca-trust\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"special-instructions-for-firefox\"\u003eSpecial Instructions for Firefox\u003c/h3\u003e\n\u003cp\u003eFirefox has its own certificate store that you can add \u003ccode\u003eca.pem\u003c/code\u003e to by accessing Preferences-\u0026gt;Privacy \u0026amp; Security-\u0026gt;Security-\u0026gt;Certificates-\u0026gt;View Certificates-\u0026gt;Authorities-\u0026gt;Import. The exact trail might have changed by the time you read this.\u003c/p\u003e\n",
    "date_published": "2020.04.18",
    "tags": ["Networking","Security"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pysubscribepattern/",
    "url": "https://brandonrozek.com/blog/pysubscribepattern/",
    "title": "Python Patterns: Subscribe",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIt is common for larger applications to have modules that publishes and subscribes to events. This post will outline a couple ways to achieve this using \u003ca href=\"/blog/pydecorators/\"\u003edecorators\u003c/a\u003e and standard methods.\u003c/p\u003e\n\u003ch2 id=\"single-event\"\u003eSingle Event\u003c/h2\u003e\n\u003cp\u003eFirst let us concern ourselves with a single event since that\u0026rsquo;s the easiest. Here we will create an application class that stores callbacks of functions through the subscribe decorator. Calling \u003ccode\u003eemit\u003c/code\u003e will send a message to all the functions stored in \u003ccode\u003eself.callbacks\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e typing \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e Callable, List\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eApplication\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __init__(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ecallbacks: List[Callable] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esubscribe\u003c/span\u003e(self, func: Callable):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e callable(func):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eValueError\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Argument func must be callable.\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ecallbacks\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend(func)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e func\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eemit\u003c/span\u003e(self, message):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e callback \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ecallbacks:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            callback(message)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHere is an example of its usage:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapp \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Application()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@app.subscribe\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etest1\u003c/span\u003e(message):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Function 1:\u0026#34;\u003c/span\u003e, message)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etest2\u003c/span\u003e(message):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Function 2:\u0026#34;\u003c/span\u003e, message)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esubscribe(test2)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eemit(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Hello World\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eFunction 1: Hello World\nFunction 2: Hello World\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"multiple-events\"\u003eMultiple Events\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s say you want the application to handle different types of events. Now \u003ccode\u003eself.callbacks\u003c/code\u003e is a dictionary of lists, where the key is the event and the list is the same as the last section. There\u0026rsquo;s an additional layered function on top of \u003ccode\u003esubscribe\u003c/code\u003e this time in order to handle passing an argument into the decorator.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e collections \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e defaultdict\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e typing \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e Callable, Optional\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eApplication\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __init__(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ecallbacks: Dict[str, List[Callable]] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e defaultdict(list)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eon\u003c/span\u003e(self, event: str, func: Optional[Callable] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esubscribe\u003c/span\u003e(func: Callable):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e callable(func):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eValueError\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Argument func must be callable.\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ecallbacks[event]\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend(func)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e func\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e func \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e subscribe\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        subscribe(func)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eemit\u003c/span\u003e(self, event, message):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e callback \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ecallbacks[event]:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            callback(message)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo show its usage lets first create an instance of \u003ccode\u003eApplication\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapp \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Application()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow let\u0026rsquo;s subscribe a couple functions to \u003ccode\u003eevent1\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@app.on\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;event1\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etest1\u003c/span\u003e(message):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Function 1:\u0026#34;\u003c/span\u003e, message)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etest3\u003c/span\u003e(message):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Function 3:\u0026#34;\u003c/span\u003e, message)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eon(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;event1\u0026#39;\u003c/span\u003e, test3)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow to subscribe a couple events to \u003ccode\u003eevent2\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Subscribed to event 2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@app.on\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;event2\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etest2\u003c/span\u003e(message):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Function 2:\u0026#34;\u003c/span\u003e, message)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etest4\u003c/span\u003e(message):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Function 4:\u0026#34;\u003c/span\u003e, message)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eon(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;event2\u0026#39;\u003c/span\u003e, test4)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can also subscribe to both events\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Subscribed to both events\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@app.on\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;event1\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@app.on\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;event2\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etest5\u003c/span\u003e(message):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Function 5:\u0026#34;\u003c/span\u003e, message)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eemit(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;event1\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Hello, World!\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eFunction 1: Hello, World!\nFunction 3: Hello, World!\nFunction 5: Hello, World!\n\u003c/code\u003e\u003c/pre\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapp\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eemit(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;event2\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Goodbye, World!\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eFunction 2: Goodbye, World!\nFunction 4: Goodbye, World!\nFunction 5: Goodbye, World!\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"alternative-observer-pattern\"\u003eAlternative: Observer Pattern\u003c/h2\u003e\n\u003cp\u003eThe above approaches assume that the callbacks would be needed for the lifetime of the program. An altnerative is to use the \u003ca href=\"https://en.wikipedia.org/wiki/Observer_pattern\"\u003eObserver design pattern\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThis assumes that we have two classes, \u003ccode\u003eObservable\u003c/code\u003e and \u003ccode\u003eObserver\u003c/code\u003e. The latter class implements the \u003ccode\u003enotify\u003c/code\u003e method and when it is constructed, it adds itself to the collection contained in the \u003ccode\u003eObservable\u003c/code\u003e class.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://martinheinz.dev/blog/112\"\u003eMartin\u003c/a\u003e wrote a great blog post on how you can use Python weak references so that when your \u003ccode\u003eObserver\u003c/code\u003e class goes out of scope, a copy isn\u0026rsquo;t stored in the \u003ccode\u003eObservable\u003c/code\u003e class and instead gets safely removed from the collection.\u003c/p\u003e\n",
    "date_published": "2020.04.14",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/downloadchanges/",
    "url": "https://brandonrozek.com/blog/downloadchanges/",
    "title": "Download Changes",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen testing daily ISO images, it is useful to only download the parts of the ISO that has changed since the previous days. That way we can preserve time and bandwidth.\u003c/p\u003e\n\u003ch2 id=\"rsync-method\"\u003eRsync method\u003c/h2\u003e\n\u003cp\u003eThis approach requires server-side support, and a lot of flags specified.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ersync --times \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --compress --partial \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --progress --stats \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --human-readable \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --human-readable \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  rsync://cdimage.ubuntu.com/cdimage/kubuntu/daily-live/20200412/focal-desktop-amd64.iso \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  kubuntu-focal-desktop-amd64.iso\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eFlag\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--times\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eTransfer modification times along with the files.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--compress\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eCompress the file data during transit.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--partial\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eKeep partial downloads if interrupted.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--progress\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003ePrint information showing the progress of the /transfer.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--stats\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003ePrint a set of statistics on the file transfer.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--human-readable\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eOutput numbers in a more human readable format, can use up to 3 times.\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"zsync-method\"\u003eZsync method\u003c/h2\u003e\n\u003cp\u003eThis approach does not involve adding a bunch of flags, but it does require the provider to have a \u003ccode\u003e.zsync\u003c/code\u003e file on their server and for the user to have the \u003ccode\u003ezync\u003c/code\u003e package installed. This package typically isn\u0026rsquo;t installed by default on most systems.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ezsync http://cdimage.ubuntu.com/kubuntu/daily-live/20200412/focal-desktop-amd64.iso.zsync\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.04.12",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/iterativecsv/",
    "url": "https://brandonrozek.com/blog/iterativecsv/",
    "title": "Iteratively Read CSV",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIf you want to analyze a CSV dataset that is larger than the space available in RAM, then you can iteratively process each observation and store/calculate only what you need. There is a way to do this in standard Python as well as the popular library Pandas.\u003c/p\u003e\n\u003ch2 id=\"standard-library\"\u003eStandard Library\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e csv\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e open(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/path/to/data.csv\u0026#39;\u003c/span\u003e, newline\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e csvfile:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   reader \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e csv\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ereader(csvfile, delimeter\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;,\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e row \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e reader:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e       \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e column \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e row:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e           do_something()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"pandas\"\u003ePandas\u003c/h2\u003e\n\u003cp\u003ePandas is slightly different in where you specify a \u003ccode\u003echunksize\u003c/code\u003e which is the number of rows per chunk and you get a pandas dataframe with that many rows\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e pandas \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e pd\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echunksize \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e chunk \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e pd\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eread_csv(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/path/to/data.csv\u0026#39;\u003c/span\u003e, chunksize\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003echunksize):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    do_something(chunk)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.04.11",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pyfutures/",
    "url": "https://brandonrozek.com/blog/pyfutures/",
    "title": "Quick Python: Concurrent Futures",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eAnother way to perform concurrency in python is to use the \u003ccode\u003econcurrent.futures\u003c/code\u003e module.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e concurrent.futures \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e ThreadPoolExecutor, ProcessPoolExecutor\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(x, y):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e x \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e y\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e ThreadPoolExecutor(max_workers\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e) \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e executor:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    future \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e executor\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esubmit(add, \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    result \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e future\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eresult(timeout\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e30\u003c/span\u003e) \u003cspan style=\"color:#75715e\"\u003e# unit: seconds\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf \u003ccode\u003emax_workers=None\u003c/code\u003e then it will default to the number of processors on the machine multiplied by 5.\u003c/p\u003e\n\u003cp\u003eIf \u003ccode\u003etimeout=None\u003c/code\u003e then there is no time limit applied.\u003c/p\u003e\n\u003cp\u003eYou can also apply a function to a list or iterables\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edouble\u003c/span\u003e(x):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e x\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e ThreadPoolExecutor() \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e executor:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    future \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e executor\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emap(function_handle, [\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e])\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    result \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e future\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eresult()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eInstead of threads, it is also possible to spawn processes to side-step the global interpreter lock. The documentation warns that only picklable objects can be executed and returned though.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(x, y):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e x \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e y\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e ProcessPoolExecutor() \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e executor:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    future \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e executor\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esubmit(add, \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    result \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e future\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eresult()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.04.11",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pipeditable/",
    "url": "https://brandonrozek.com/blog/pipeditable/",
    "title": "Pip Editable",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve found it to be incredibly helpful to emulate having a library installed on my system rather than depending on my local directory path to pick up my file edits. To do this in a python project, we need to add the \u003ccode\u003e--editable\u003c/code\u003e flag to a pip install.\u003c/p\u003e\n\u003cp\u003eFirst uninstall whatever version of your \u003ccode\u003elibrary\u003c/code\u003e you have.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip uninstall library\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen in your folder with the \u003ccode\u003esetup.py\u003c/code\u003e run the following command\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install --editable .\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will then create a symlink from your site-packages directory to the directory in which your code lives.\u003c/p\u003e\n\u003cp\u003eOnce you\u0026rsquo;re ready to install it formally,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip uninstall library\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install .\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDistribute it,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip wheel .\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.04.11",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/signingcommits/",
    "url": "https://brandonrozek.com/blog/signingcommits/",
    "title": "Signing Commits",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eGit and their various hosting platforms support commit signing as an additional step of verification. There seems to be an active debate on whether it should be used regularly, though I\u0026rsquo;ll describe it on here in case you want to set it up.\u003c/p\u003e\n\u003cp\u003eYou\u0026rsquo;ll need to have a \u003ca href=\"/blog/gpgkeygen/\"\u003eGPG key already created\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eFirst locate the key you want to sign with\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egpg --list-secret-keys --keyid-format SHORT\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will output something like\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/home/user/.gnupg/pubring.kbx\n------------------------------\nsec   rsa4096/8294756F 2020-04-11 [SC] [expires: 2021-04-11]\n      KDIAUBEUX837DIU79YHDKAPOEMNCD7123FDAPOI\nuid         [ultimate] Brandon Rozek (Git)\nssb   rsa4096/9582109R 2020-04-11 [E] [expires: 2021-04-11]\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIf you want to sign your commits with your main private key, then you can use the main\nkey\u0026rsquo;s \u003cem\u003efingerprint\u003c/em\u003e. In the example above, that\u0026rsquo;s the part that starts with \u003ccode\u003eKDI\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"optional-creating-a-signing-subkey\"\u003e(Optional) Creating a signing subkey\u003c/h2\u003e\n\u003cp\u003eAlternatively, we can create a subkey specifically for signing commits.\nTo do that, first we need to enter the edit mode for that key.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egpg --edit-key $FINGERPRINT\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003ewhere \u003ccode\u003e$FINGERPRINT\u003c/code\u003e is the same fingerprint above.\u003c/p\u003e\n\u003cp\u003eYou\u0026rsquo;ll see something like the following\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esec  rsa3072/3E40C8DB05FCCFAD\n     created: 2022-12-18  expires: 2023-12-18       usage: SC  \n     trust: ultimate      validity: ultimate\nssb  rsa3072/50CC6B37C26F7882\n     created: 2022-12-18  expires: 2023-12-18       usage: E   \n[ultimate] (1). Brandon Rozek\n\ngpg\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFrom there you type \u003ccode\u003eaddkey\u003c/code\u003e, which will then present you with some options\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ePlease select what kind of key you want:\n   (3) DSA (sign only)\n   (4) RSA (sign only)\n   (5) Elgamal (encrypt only)\n   (6) RSA (encrypt only)\n  (10) ECC (sign only)\n  (12) ECC (encrypt only)\n  (14) Existing key from card\nYour selection? \n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAs before, I recommend going with the default signing key option.\nI recently chose \u003ccode\u003e(10) ECC (sign only)\u003c/code\u003e.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ePlease select which elliptic curve you want:\n   (1) Curve 25519 *default*\n   (4) NIST P-384\n   (6) Brainpool P-256\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAs before, either stick with the default or tweak based\non your personal assesment of risk.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ePlease specify how long the key should be valid.\n         0 = key does not expire\n      \u0026lt;n\u0026gt;  = key expires in n days\n      \u0026lt;n\u0026gt;w = key expires in n weeks\n      \u0026lt;n\u0026gt;m = key expires in n months\n      \u0026lt;n\u0026gt;y = key expires in n years\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eSame advice as before in terms of key expiration.\nI generally stick with \u003ccode\u003e1y\u003c/code\u003e. Then, after\nconfirming the sanity checks you should see the key created.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esec  rsa3072/3E40C8DB05FCCFAD\n     created: 2022-12-18  expires: 2023-12-18   usage: SC  \n     trust: ultimate      validity: ultimate\nssb  rsa3072/50CC6B37C26F7882\n     created: 2022-12-18  expires: 2023-12-18   usage: E \nssb  ed25519/5C1B6FCA0DABB046\n     created: 2022-12-18  expires: 2023-12-18   usage: S   \n[ultimate] (1). TestKey\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eThe signing key is denoted by the label \u003ccode\u003eusage: S\u003c/code\u003e.\nFrom there we can take its fingerprint, which for\nthe example above starts with \u003ccode\u003e5C1B\u003c/code\u003e and proceed\nwith the next step.\u003c/p\u003e\n\u003ch2 id=\"configuring-git\"\u003eConfiguring Git\u003c/h2\u003e\n\u003cp\u003eFrom here, we need to tell git the key we want to sign with\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit config --global user.signingkey $FINGERPRINT\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo sign a commit, add a \u003ccode\u003e-S\u003c/code\u003e flag\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit commit -S -m \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Initial Commit\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo always sign your commits\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit config --global commit.gpgsign true\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRemember to add your public key to Github, Gitlab, etc. You can get it by\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egpg --armor --export $FINGERPRINT\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.04.11",
    "tags": ["Git","GPG/PGP"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/gpgkeygen/",
    "url": "https://brandonrozek.com/blog/gpgkeygen/",
    "title": "GPG Keygen",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eGPG keys have a variety of different uses from sending encrypted emails to verifying git commits. Here I\u0026rsquo;ll show how to create a public/private key-pair. This post assumes you have the \u003ccode\u003egpg\u003c/code\u003e client installed.\u003c/p\u003e\n\u003cp\u003eType the following command\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egpg --full-gen-key\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will then show\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egpg (GnuPG) 2.2.12; Copyright (C) 2018 Free Software Foundation, Inc.\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law.\n\nPlease select what kind of key you want:\n   (1) RSA and RSA (default)\n   (2) DSA and Elgamal\n   (3) DSA (sign only)\n   (4) RSA (sign only)\nYour selection? 1\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eI generally recommend selecting the default option. As cryptography standards change, I would expect the options presented to you to differ from my selection screen.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eRSA keys may be between 1024 and 4096 bits long.\nWhat keysize do you want? (3072) 4096\nRequested keysize is 4096 bits\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFor keysizes, the bigger the more secure. The tradeoff is in the time it takes to perform the cryptographic operations.\nSince I rarely encrypted very large inputs, I went for the highest available option as the encryption time to me\nis negligable.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ePlease specify how long the key should be valid.\n         0 = key does not expire\n      \u0026lt;n\u0026gt;  = key expires in n days\n      \u0026lt;n\u0026gt;w = key expires in n weeks\n      \u0026lt;n\u0026gt;m = key expires in n months\n      \u0026lt;n\u0026gt;y = key expires in n years\nKey is valid for? (0) 1y\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eI highly recommend that you set an expiration date. Not only does this allow for the key to become invalid if you happen to\nlose your private key, it also announces to the wider world that you actually use your GPG key.\u003c/p\u003e\n\u003cp\u003eI try to set my key expiration dates to be a year out.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eKey expires at Mon 11 Apr 2021 06:42:01 PM EDT\nIs this correct? (y/N) y\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAs a quick sanity check, it\u0026rsquo;ll provide the date that the key will expire.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eGnuPG needs to construct a user ID to identify your key.\n\nReal name: Brandon Rozek\nEmail address:\nComment: Git\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAll the fields are optional.  Fill them in as you wish.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eYou selected this USER-ID:\n    \u0026#34;Brandon Rozek (Git)\u0026#34;\nChange (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eAnother sanity check, to ensure that you set your user information correctly.\nKeep in mind that this information is included in plaintext as part of your public key.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eWe need to generate a lot of random bytes. It is a good idea to perform\nsome other action (type on the keyboard, move the mouse, utilize the\ndisks) during the prime generation; this gives the random number\ngenerator a better chance to gain enough entropy.\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eDo as it says, start going crazy.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egpg: key IFMWDHXYSKTICHSE marked as ultimately trusted\npublic and secret key created and signed.\n\npub   rsa4096 2020-04-11 [SC] [expires: 2021-04-11]\n      FDMGHVYEIWPDKVT83ICUZOEKSLFIVYQALKZMNTYR\nuid                      Brandon Rozek (Git)\nsub   rsa4096 2020-04-11 [E] [expires: 2021-04-11]\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eCongratulations, you now have a GPG key set.\u003c/p\u003e\n",
    "date_published": "2020.04.11",
    "tags": ["GPG/PGP"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pipconf/",
    "url": "https://brandonrozek.com/blog/pipconf/",
    "title": "Pip Config",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIf you find yourself added flags to every pip command, consider adding those flag\u0026rsquo;s to a pip configuration file.\u003c/p\u003e\n\u003cp\u003eIn order of importance, the configuration files will be located\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eInside the virtualenv \u003ccode\u003e/path/to/virtualenv/pip.conf\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eIn the user folder \u003ccode\u003e~/.config/pip/pip.conf\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eSite-wide \u003ccode\u003e/etc/pip.conf\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIt is structured as an INI file where the blocks are the commands (\u003ccode\u003eglobal\u003c/code\u003e indicates all commands)\u003c/p\u003e\n\u003cp\u003eFor an example, we can set the timeout for all commands to 60 seconds,  but the timeout for the freeze command to only 10 seconds.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[global]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003etimeout\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e60\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[freeze]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003etimeout\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e10\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBoolean flags are set by assigning a value of \u003ccode\u003etrue\u003c/code\u003e or \u003ccode\u003eyes\u003c/code\u003e to them\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eignore-installed\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor operating in an offline environment,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[global]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eno-index\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003efind-links\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/path/to/wheels\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.04.10",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/gevent/",
    "url": "https://brandonrozek.com/blog/gevent/",
    "title": "Simple Async in Python with Gevent",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn my last post I spoke about \u003ca href=\"/blog/pyasyncio/\"\u003econcurrency with asyncio\u003c/a\u003e. Now what if you don\u0026rsquo;t want to concern yourself with async/await practices and just want to write synchronous code that executes I/O asynchronously?  That\u0026rsquo;s where the library \u003ca href=\"http://www.gevent.org/\"\u003egevent\u003c/a\u003e comes in. It does this by modifying Python\u0026rsquo;s standard library during runtime to call it\u0026rsquo;s own asynchronous versions.\u003c/p\u003e\n\u003cp\u003eLast post code\u0026rsquo;s example written in \u003ccode\u003egevent\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# The first two lines must be called before\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# any other modules are loaded\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e gevent\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e gevent \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e monkey; monkey\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epatch_all()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e time\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ethink\u003c/span\u003e(duration):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Starting to think for \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e str(duration) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; seconds...\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    time\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esleep(duration)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Finished thinking for \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e str(duration) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; seconds...\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egevent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ewait([\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    gevent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003espawn(think, \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    gevent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003espawn(think, \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e])\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNotice that the function \u003ccode\u003ethink\u003c/code\u003e is written the same as the synchronous version.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003egevent\u003c/code\u003e is written on top of C libraries \u003ccode\u003elibev\u003c/code\u003e or \u003ccode\u003elibuv\u003c/code\u003e . This combined with the monkey patching can make \u003ccode\u003egevent\u003c/code\u003e based applications hard to debug if something goes wrong. Otherwise it\u0026rsquo;s a great tool to quickly take advantage of concurrency.\u003c/p\u003e\n",
    "date_published": "2020.04.09",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pyasyncio/",
    "url": "https://brandonrozek.com/blog/pyasyncio/",
    "title": "Python asyncio",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eDaniel Pope wrote a \u003ca href=\"http://mauveweb.co.uk/posts/2014/07/gevent-asynchronous-io-made-easy.html\"\u003egreat blog post\u003c/a\u003e describing the different ways of performing asynchronous I/O in Python. In this post, I want to focus on his section called \u0026ldquo;Generator-based Coroutine\u0026rdquo;. Python\u0026rsquo;s \u003ccode\u003easyncio\u003c/code\u003e module in the standard library has a concept of \u0026ldquo;coroutines\u0026rdquo; that uses generators instead of callbacks or promises seen in other asynchronous frameworks.\u003c/p\u003e\n\u003cp\u003eWhenever a I/O call is made, you can prepend the statement with \u003ccode\u003eyield from\u003c/code\u003e and as long as the function is under the \u003ccode\u003easyncio.coroutine\u003c/code\u003e generator, the Python module will handle the call asynchronously. Now this method doesn\u0026rsquo;t fully fit under the coroutine framework since generators can only yield to the caller frame. Therefore, it is called a \u003ca href=\"https://en.wikipedia.org/wiki/Coroutine#Comparison_with_generators\"\u003esemicoroutine\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eHere is an example of its usage\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e asyncio\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@asyncio.coroutine\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ethink\u003c/span\u003e(duration):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Starting to think for \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e str(duration) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; seconds...\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eyield from\u003c/span\u003e asyncio\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esleep(duration)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Finished thinking for \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e str(duration) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; seconds...\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eloop \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e asyncio\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eget_event_loop()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eloop\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003erun_until_complete(asyncio\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003egather(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    think(\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    think(\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eloop\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eclose()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOutput:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eStarting to think for 5 seconds...\nStarting to think for 2 seconds...\nFinished thinking for 2 seconds...\nFinished thinking for 5 seconds...\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"newer-syntax\"\u003eNewer Syntax\u003c/h2\u003e\n\u003cp\u003eNow the decorator syntax has been deprecated in Python 3.8 in favor of the more common \u003ccode\u003easync\u003c/code\u003e /\u003ccode\u003eawait\u003c/code\u003e syntax introduced in Python 3.7.  The reason I showed the previous version is because I think it\u0026rsquo;s important to understand how this module is implemented behind the scenes. Also since Python 3.6 code is likely still floating around,  you might encounter code bases like the above in your day to day.\u003c/p\u003e\n\u003cp\u003eHere is the equivalent code written in modern day syntax\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e asyncio\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003easync\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ethink\u003c/span\u003e(duration):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Starting to think for \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e str(duration) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; seconds...\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eawait\u003c/span\u003e asyncio\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esleep(duration)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Finished thinking for \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e str(duration) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; seconds...\u0026#34;\u003c/span\u003e) \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003easync\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eawait\u003c/span\u003e asyncio\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003egather(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        think(\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        think(\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003easyncio\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003erun(main())\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNot that much shorter in terms of lines of code, but it allows the developer to not have to be concerned about the event loop or generators.\u003c/p\u003e\n",
    "date_published": "2020.04.09",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pydataclass/",
    "url": "https://brandonrozek.com/blog/pydataclass/",
    "title": "Quick Python: Dataclasses",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003ePython 3.7 and above have a feature called dataclasses. This allows us to reduce boilerplate code by removing the need to create a whole constructor and providing a sensible \u003ccode\u003e__repr__\u003c/code\u003e function.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e dataclasses \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e dataclass\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@dataclass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ePerson\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    name: str\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    age: int \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eUsage:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ep \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Person(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Bob\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e30\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(p)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ePerson(name=\u0026#39;Bob\u0026#39;, age=20)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eTo make an attribute have a default value, add it after the type declaration like I have with \u003ccode\u003eage\u003c/code\u003e.\u003c/p\u003e\n",
    "date_published": "2020.04.08",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/copydecorator/",
    "url": "https://brandonrozek.com/blog/copydecorator/",
    "title": "Quick Python: Copy Decorator",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIf you want to guarantee that your function doesn\u0026rsquo;t modify any of the references and don\u0026rsquo;t mind paying a price in memory consumption, here is a decorator you can easily add to your functions.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e copy \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e deepcopy\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecopy_arguments\u003c/span\u003e(func):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ewrapper\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eargs, \u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003ekwargs):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        new_args \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e deepcopy(args)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        new_kwargs \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e deepcopy(kwargs)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e func(\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003enew_args, \u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003enew_kwargs)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e wrapper\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eExample usage:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@copy_arguments\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emodify1\u003c/span\u003e(xs):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e i, _ \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e enumerate(xs):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        xs[i] \u003cspan style=\"color:#f92672\"\u003e*=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eComparison:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emodify2\u003c/span\u003e(xs):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e i, _ \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e enumerate(xs):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        xs[i] \u003cspan style=\"color:#f92672\"\u003e*=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ea \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eb \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emodify1(a)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emodify2(a)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(a)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(b)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e[1, 2, 3, 4, 5]\n[2, 4, 6, 8, 10]\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2020.04.08",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pygetset/",
    "url": "https://brandonrozek.com/blog/pygetset/",
    "title": "Quick Python: Getters and Setters",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOne of the hidden gems in Python classes are seamless getters and setters. I discovered this through the book \u003ca href=\"https://effectivepython.com/\"\u003eEffective Python by Brett Slatkin\u003c/a\u003e. Though the example I\u0026rsquo;ll use is different and shorter than the one he uses in his book.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s create a class representing a person. The only information we\u0026rsquo;re going to store is their age and we\u0026rsquo;ll make it optional to provide it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ePerson\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __init__(self, age\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e_age \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003e@property\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eage\u003c/span\u003e(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e_age \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNone\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eValueError\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;age must be set before accessing it.\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e_age\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   \t\u003cspan style=\"color:#a6e22e\"\u003e@age.setter\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eage\u003c/span\u003e(self, age):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e age \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eraise\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eValueError\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;age must be at least zero.\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e_age \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e age\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe second function in the class decorated by \u003ccode\u003e@property\u003c/code\u003e will be the getter function for the attribute \u003ccode\u003e_age\u003c/code\u003e. The name of the function will be what we expect the user to access it as. The setter is then decorated with \u003ccode\u003eage.setter\u003c/code\u003e where \u003ccode\u003eage\u003c/code\u003e is the name of the attribute. As such the name chosen in the getter function name, setter function name, and decorator must all match.\u003c/p\u003e\n\u003cp\u003eNow let\u0026rsquo;s try using it\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebobby \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Person()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebobby\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eage\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eTraceback (most recent call last):\n  File \u0026#34;\u0026lt;stdin\u0026gt;\u0026#34;, line 1, in \u0026lt;module\u0026gt;\n  File \u0026#34;/home/user/test.py\u0026#34;, line 7, in age\n    raise ValueError(\u0026#34;age must first be set before accessing it\u0026#34;)\nValueError: age must first be set before accessing it\n\u003c/code\u003e\u003c/pre\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebobby\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eage \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ebobby\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eage\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e5\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2020.04.08",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pyunittest/",
    "url": "https://brandonrozek.com/blog/pyunittest/",
    "title": "Quick Python: Unit Testing",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003ePython has a great built-in \u003ca href=\"https://docs.python.org/3.7/library/unittest.html\"\u003eunit test\u003c/a\u003e framework. This post will give a skeleton for how to format the files in your \u003ccode\u003etests\u003c/code\u003e directory.\u003c/p\u003e\n\u003cp\u003eExample \u003ccode\u003etests/test_basic.py\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e unittest\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eTester\u003c/span\u003e(unittest\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eTestCase):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esetUp\u003c/span\u003e(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;To Run Before Every Test Case\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003epass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etearDown\u003c/span\u003e(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;To Run After Every Test Case\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003epass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etest_something\u003c/span\u003e(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;A Test Case\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eassertEqual(\u003cspan style=\"color:#66d9ef\"\u003eTrue\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003eTrue\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e __name__ \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;__main__\u0026#39;\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    unittest\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emain()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo auto-discover and run your tests\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epython -m unittest discover -s tests\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.03.31",
    "tags": ["Python","Testing"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pydecorators/",
    "url": "https://brandonrozek.com/blog/pydecorators/",
    "title": "Quick Python: Decorators",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://realpython.com/team/gahjelle/\"\u003eGeir Arne Hjelle\u003c/a\u003e at Real Python wrote a great post called \u003ca href=\"https://realpython.com/primer-on-python-decorators/\"\u003ePrimer on Python Decorators\u003c/a\u003e. I recommend reading that as this post serves mostly as a reminder to myself on how to write a decorator.\u003c/p\u003e\n\u003cp\u003eI find decorators useful for several reasons\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eCheck a pre-existing condition (Is the user logged in?)\u003c/li\u003e\n\u003cli\u003ePerform post processing on function output (Convert to SI units)\u003c/li\u003e\n\u003cli\u003eExpose extra variables for use in the function\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eHere is a template for how a decorator is written\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e functools\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edecorator\u003c/span\u003e(func):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003e@functools.wraps\u003c/span\u003e(func)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ewrapper\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eargs, \u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003ekwargs):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# Do something before\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        value \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e func(\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eargs, \u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003ekwargs)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# Do something after\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e value\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e wrapper\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf your decorator takes arguments then there\u0026rsquo;s another layer\u0026hellip;\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edecorator_with_argument\u003c/span\u003e(argument):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edecorator\u003c/span\u003e(func):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003e@functools.wraps\u003c/span\u003e(func)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ewrapper\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eargs, \u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003ekwargs):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#75715e\"\u003e# Do something before\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            value \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e func(\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eargs, \u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003ekwargs)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#75715e\"\u003e# Do something after\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e value\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e wrapper\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e decorator\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"example-logging\"\u003eExample: Logging\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s write a decorator that logs the string returned by the function to a file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efilelog\u003c/span\u003e(filename):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edecorator\u003c/span\u003e(func):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003e@functools.wraps\u003c/span\u003e(func)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ewrapper\u003c/span\u003e(\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eargs, \u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003ekwargs):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e open(filename, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;w\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e f:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                f\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ewrite(func(\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eargs, \u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003ekwargs))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e wrapper\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e decorator\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@filelog\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;log.txt\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003egreet\u003c/span\u003e(name):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ef\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Hello \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{\u003c/span\u003ename\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e!\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.03.30",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pymemoization/",
    "url": "https://brandonrozek.com/blog/pymemoization/",
    "title": "Quick Python: Memoization",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThere is often a trade-off when it comes to efficiency of CPU vs memory usage. In this post, I will show how the \u003ca href=\"https://docs.python.org/3/library/functools.html#functools.lru_cache\"\u003e\u003ccode\u003elru_cache\u003c/code\u003e\u003c/a\u003e decorator can cache results of a function call for quicker future lookup.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e functools \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e lru_cache\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@lru_cache\u003c/span\u003e(maxsize\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e7\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efib\u003c/span\u003e(n):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e f(n \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e f(n \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn the code above, \u003ccode\u003emaxsize\u003c/code\u003e indicates the number of calls to store. Setting it to \u003ccode\u003eNone\u003c/code\u003e will make it so that there is no upper bound. The documentation recommends setting it equal to a power of two.\u003c/p\u003e\n\u003cp\u003eDo note though that \u003ccode\u003elru_cache\u003c/code\u003e does not make the execution of the lines in the function faster. It only stores the results of the function in a dictionary.\u003c/p\u003e\n",
    "date_published": "2020.03.30",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/bdaymusic/",
    "url": "https://brandonrozek.com/blog/bdaymusic/",
    "title": "Birthday Music",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI was scrolling around in the \u003ca href=\"https://freemusicarchive.org/\"\u003eFree Music Archive\u003c/a\u003e and I stumbled upon the entries of their \u003ca href=\"https://www.freemusicarchive.org/music/Happy_Birthday_Song_Contest/The_New_Birthday_Song_Contest\"\u003eThe New Birthday Song Contest\u003c/a\u003e back in 2012. One of my favorites is an entry by \u003ca href=\"http://www.kevinlax.com/\"\u003eKevin Lax\u003c/a\u003e titled \u003ca href=\"https://files.freemusicarchive.org/storage-freemusicarchive-org/music/WFMU/Kevin_Lax/The_New_Birthday_Song_Contest/Kevin_Lax_-_Happy_Birthday.mp3\"\u003eHappy Birthday\u003c/a\u003e licensed under \u003ca href=\"https://creativecommons.org/licenses/by/3.0/\"\u003eCreative Commons Attributioni v3\u003c/a\u003e.\u003c/p\u003e\n\u003caudio controls src=\"https://files.freemusicarchive.org/storage-freemusicarchive-org/music/WFMU/Kevin_Lax/The_New_Birthday_Song_Contest/Kevin_Lax_-_Happy_Birthday.mp3\"\u003e\n\u003c/audio\u003e\n\u003cp\u003eEven though the competition is long over (and I don\u0026rsquo;t have much skill) I still decided to record a small entry which I\u0026rsquo;ll title \u003ca href=\"/files/music/happybday.ogg\"\u003eHappy Birthday\u003c/a\u003e licensed under \u003ca href=\"https://creativecommons.org/licenses/by/4.0/\"\u003eCreative Commons Attribution v4\u003c/a\u003e.\u003c/p\u003e\n\u003caudio controls src=\"/files/music/happybday.ogg\"\u003e\n\u003c/audio\u003e\n",
    "date_published": "2020.03.29",
    "tags": ["Music"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/discgolfpymc/",
    "url": "https://brandonrozek.com/blog/discgolfpymc/",
    "title": "Disc Golf and PyMC3",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve been following along with \u003ca href=\"https://github.com/CamDavidsonPilon/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers/tree/master/\"\u003eBayesian Methods for Hackers\u003c/a\u003e and I wanted to try using PyMC3 with my own small dataset.\u003c/p\u003e\n\u003cp\u003eA week ago, a couple friends and I went out and played Disc Golf at a local park. In case you don\u0026rsquo;t know what Disc Golf is, the goal is for a player to throw a disc at a target with as few throws as possible. Throughout each of the \u003cem\u003eholes\u003c/em\u003e, I recorded the number of tosses required until we completed that hole.\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eBrandon\u003c/th\u003e\n          \u003cth\u003eChris\u003c/th\u003e\n          \u003cth\u003eClare\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e6\u003c/td\u003e\n          \u003ctd\u003e8\u003c/td\u003e\n          \u003ctd\u003e10\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e7\u003c/td\u003e\n          \u003ctd\u003e9\u003c/td\u003e\n          \u003ctd\u003e10\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e7\u003c/td\u003e\n          \u003ctd\u003e8\u003c/td\u003e\n          \u003ctd\u003e9\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e7\u003c/td\u003e\n          \u003ctd\u003e7\u003c/td\u003e\n          \u003ctd\u003e8\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e5\u003c/td\u003e\n          \u003ctd\u003e4\u003c/td\u003e\n          \u003ctd\u003e9\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e5\u003c/td\u003e\n          \u003ctd\u003e5\u003c/td\u003e\n          \u003ctd\u003e10\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e4\u003c/td\u003e\n          \u003ctd\u003e4\u003c/td\u003e\n          \u003ctd\u003e7\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e5\u003c/td\u003e\n          \u003ctd\u003e6\u003c/td\u003e\n          \u003ctd\u003e9\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e6\u003c/td\u003e\n          \u003ctd\u003e5\u003c/td\u003e\n          \u003ctd\u003e7\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e7\u003c/td\u003e\n          \u003ctd\u003e7\u003c/td\u003e\n          \u003ctd\u003e8\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e5\u003c/td\u003e\n          \u003ctd\u003e6\u003c/td\u003e\n          \u003ctd\u003e8\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e6\u003c/td\u003e\n          \u003ctd\u003e5\u003c/td\u003e\n          \u003ctd\u003e7\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e6\u003c/td\u003e\n          \u003ctd\u003e6\u003c/td\u003e\n          \u003ctd\u003e8\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e5\u003c/td\u003e\n          \u003ctd\u003e4\u003c/td\u003e\n          \u003ctd\u003e6\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eYou can also \u003ca href=\"/data/discgolf03242020.csv\"\u003edownload the CSV\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eWhat I want to know is the distribution of the number of tosses for each player.\u003c/p\u003e\n\u003ch2 id=\"pymc3\"\u003ePyMC3\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s try answering this with Bayesian Statistics + PyMC3.\u003c/p\u003e\n\u003cp\u003eFirst we\u0026rsquo;ll need to import the relevant packages\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e pandas \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e pd\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e pymc3 \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e pm\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e matplotlib.pyplot \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e plt\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e numpy \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e np\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e scipy.stats \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e poisson\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLoad in the data\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDISC_DATA \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e pd\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eread_csv(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;data/discgolf03242020.csv\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePEOPLE \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Chris\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Clare\u0026#34;\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSince the number of tosses are count data, we are going to use the \u003ca href=\"https://en.wikipedia.org/wiki/Poisson_distribution\"\u003ePoisson distribution\u003c/a\u003e for the estimation. This means that we are going to characterize each player\u0026rsquo;s number of tosses with this distribution. The Poisson distribution has a  parameter $\\lambda$ which also serves as the \u003ca href=\"https://en.wikipedia.org/wiki/Expected_value\"\u003eexpected value\u003c/a\u003e. The shape of the distribution is dependent on this parameter, so we\u0026rsquo;ll need to estimate what this parameter is. Since the expected value must be positive, the exponential distribution is a good candidate.\n$$\ntoss \\sim Poisson(\\lambda) \\\n\\lambda \\sim Exp(\\alpha)\n$$\nThe exponential distribution also has a parameter  $\\alpha$.  The expected value of an exponential distribution with respect to $\\alpha$ is $\\frac{1}{\\alpha}$. At this point we can give an estimate of what we believe $\\alpha$ could be. Given the relationships with the expected values, the mean score of each of the players is a great choice.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e pm\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eModel() \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e model:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Random Variables\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ALPHA \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#ae81ff\"\u003e1.\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e DISC_DATA[person]\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emean() \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e person \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e PEOPLE]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    LAMBDAS \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [pm\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eExponential(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;lambda_\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e person, alpha) \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e person, alpha \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e zip(PEOPLE, ALPHAS)]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow to show how easy the library is, we will provide the data and run Monte Carlo simulations to see the distribution that the $\\lambda$s live in.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e model:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    OBS \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        pm\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ePoisson(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;obs_\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e person, lambda_, observed\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eDISC_DATA[person])\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e person, lambda_ \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e zip(PEOPLE, LAMBDAS)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    TRACES \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e pm\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esample(\u003cspan style=\"color:#ae81ff\"\u003e10000\u003c/span\u003e, tune\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e5000\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can then grab the distribution of $\\lambda$s from the trace.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLAMBDA_SAMPLES \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [TRACE[\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;lambda_\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e person] \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e person \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e PEOPLE]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"visualization\"\u003eVisualization\u003c/h2\u003e\n\u003cp\u003eFirst let\u0026rsquo;s check out the average disc tosses for each player by looking at how the $\\lambda$s are distributed.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efigure(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Distribution of Average Disc Tosses\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e person, lambda_sample, color \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e zip(PEOPLE, LAMBDA_SAMPLES, COLORS):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    plt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehist(lambda_sample,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        histtype\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;stepfilled\u0026#39;\u003c/span\u003e, bins\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e30\u003c/span\u003e, alpha\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0.85\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        label\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003er\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;posterior of $\\lambda_{\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e person \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;}$\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        color\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ecolor, density\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eTrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePARAMETER_TITLE \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003er\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\\;\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ejoin(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    [\u003cspan style=\"color:#e6db74\"\u003er\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\\lambda_{\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e person \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;}\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e person \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e PEOPLE]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003etitle(\u003cspan style=\"color:#e6db74\"\u003ef\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;Posterior distributions of the variables\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    $\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{\u003c/span\u003ePARAMETER_TITLE\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e$\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003exlim([\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DISC_DATA[PEOPLE]\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emin()\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emin() \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e, \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DISC_DATA[PEOPLE]\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emax()\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emax() \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e])\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elegend()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003exlabel(\u003cspan style=\"color:#e6db74\"\u003er\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;$\\lambda$\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eshow()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/2020032901.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eNow let\u0026rsquo;s look at the distribution of the number of tosses for each player\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efigure(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Distribution of Disc Tosses\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e person, lambda_sample, color \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e zip(PEOPLE, LAMBDA_SAMPLES, COLORS):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    tosses \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e np\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003earange(\u003cspan style=\"color:#ae81ff\"\u003e15\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    lambda_ \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e lambda_sample\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emean()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    plt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebar(tosses, poisson\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epmf(tosses, lambda_), color\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ecolor,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            label\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003er\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;$\\lambda_{\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e person \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;}$ = \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{:0.1f}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eformat(lambda_),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            alpha\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0.60\u003c/span\u003e, edgecolor\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ecolor, lw\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;3\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elegend()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003exlabel(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Number of Tosses\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003etitle(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Poisson Distributions\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/2020032902.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eWe can see in the first graphic that the average number of tosses required for Brandon and Chris is about $6$, while for Clare it\u0026rsquo;s $8$. Looking at the second graphic though, shows us that Clare has a wide range of possible tosses.\u003c/p\u003e\n\u003cp\u003eWith PyMC3, we got to see the larger trends in the data by analyzing distributions that are more likely given the data.\u003c/p\u003e\n\u003cp\u003eFor easy reference, here is the entire script.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e pandas \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e pd\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e pymc3 \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e pm\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e matplotlib.pyplot \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e plt\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e numpy \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e np\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e scipy.stats \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e poisson\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDISC_DATA \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e pd\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eread_csv(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;data/discgolf03242020.csv\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePEOPLE \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Chris\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Clare\u0026#34;\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCOLORS \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;#A60628\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;#7A68A6\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;#4c9e65\u0026#34;\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eassert\u003c/span\u003e len(PEOPLE) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e len(COLORS)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewith\u003c/span\u003e pm\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eModel() \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e model:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Random Variables\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ALPHAS \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#ae81ff\"\u003e1.\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e DISC_DATA[person]\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emean() \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e person \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e PEOPLE]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    LAMBDAS \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [pm\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eExponential(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;lambda_\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e person, alpha) \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e person, alpha \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e zip(PEOPLE, ALPHAS)]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Observations\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    OBS \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        pm\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ePoisson(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;obs_\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e person, lambda_, observed\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eDISC_DATA[person]) \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e person, lambda_ \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e zip(PEOPLE, LAMBDAS)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Monte-Carlo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    TRACE \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e pm\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esample(\u003cspan style=\"color:#ae81ff\"\u003e10000\u003c/span\u003e, tune\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e5000\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLAMBDA_SAMPLES \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [TRACE[\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;lambda_\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e person] \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e person \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e PEOPLE]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Graph histogram of samples\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efigure(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Distribution of Average Disc Tosses\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e person, lambda_sample, color \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e zip(PEOPLE, LAMBDA_SAMPLES, COLORS):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    plt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehist(lambda_sample,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        histtype\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;stepfilled\u0026#39;\u003c/span\u003e, bins\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e30\u003c/span\u003e, alpha\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0.85\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        label\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003er\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;posterior of $\\lambda_{\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e person \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;}$\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        color\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ecolor, density\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eTrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePARAMETER_TITLE \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003er\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\\;\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ejoin(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    [\u003cspan style=\"color:#e6db74\"\u003er\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\\lambda_{\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e person \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;}\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e person \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e PEOPLE]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003etitle(\u003cspan style=\"color:#e6db74\"\u003ef\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;Posterior distributions of the variables\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    $\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{\u003c/span\u003ePARAMETER_TITLE\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e$\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003exlim([\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DISC_DATA[PEOPLE]\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emin()\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emin() \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e, \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DISC_DATA[PEOPLE]\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emax()\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emax() \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e])\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elegend()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003exlabel(\u003cspan style=\"color:#e6db74\"\u003er\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;$\\lambda$\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Graph Poisson Distributions\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efigure(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Distribution of Disc Tosses\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e person, lambda_sample, color \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e zip(PEOPLE, LAMBDA_SAMPLES, COLORS):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    tosses \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e np\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003earange(\u003cspan style=\"color:#ae81ff\"\u003e15\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    lambda_ \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e lambda_sample\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003emean()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    plt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebar(tosses, poisson\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epmf(tosses, lambda_), color\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ecolor,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            label\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003er\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;$\\lambda_{\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e person \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;}$ = \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{:0.1f}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eformat(lambda_),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            alpha\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0.60\u003c/span\u003e, edgecolor\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ecolor, lw\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;3\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003elegend()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003exlabel(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Number of Tosses\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003etitle(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Poisson Distributions\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eplt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eshow()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.03.28",
    "tags": ["Python","Statistics"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pyleniterables/",
    "url": "https://brandonrozek.com/blog/pyleniterables/",
    "title": "Quick Python: Length of Iterables",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI wanted to find the length of what I know is a finite iterable. Normally you would think of using the \u003ccode\u003elen\u003c/code\u003e function but it does not work in this case. \u003ca href=\"https://stackoverflow.com/a/44351664\"\u003eAl Hoo\u003c/a\u003e on StackOverflow shared a quick snippet to calculate this.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e functools \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e reduce\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eilen\u003c/span\u003e(iterable):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e reduce(\u003cspan style=\"color:#66d9ef\"\u003elambda\u003c/span\u003e sum, element: sum \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, iterable, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis also turns out to be memory efficient since we are only loading in one object into memory from the iterable at a time.\u003c/p\u003e\n",
    "date_published": "2020.03.25",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pyenvbuildflags/",
    "url": "https://brandonrozek.com/blog/pyenvbuildflags/",
    "title": "Pyenv Build Flags",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI ran into an issue with PyMC3 where it was expecting a certain symbol that Python wasn\u0026rsquo;t compiled with.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eException: Compilation failed (return status=1): /usr/bin/ld: /home/rozek/.pyenv/versions/3.8.2/lib/libpython3.8.a(floatobject.o): relocation R_X86_64_PC32 against symbol `PyFloat_Type\u0026#39; can not be used when making a shared object; recompile with -fPIC. /usr/bin/ld: final link failed: bad value. collect2: error: ld returned 1 exit status. \n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eSince I use \u003ca href=\"/blog/pyenv/\"\u003e\u003ccode\u003epyenv\u003c/code\u003e\u003c/a\u003e for Python version management, it turned out that I only needed to modify the \u003ccode\u003eCFLAGS\u003c/code\u003e variable to get this working\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCFLAGS\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;-fPIC\u0026#34;\u003c/span\u003e pyenv install 3.8.2\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTurns out you can configure other flags during Python installation as well such as \u003ccode\u003eCPPFLAGS\u003c/code\u003e and \u003ccode\u003eLDFLAGS\u003c/code\u003e.\u003c/p\u003e\n",
    "date_published": "2020.03.24",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/uninstallvimplugins/",
    "url": "https://brandonrozek.com/blog/uninstallvimplugins/",
    "title": "Uninstall Vim Plugins",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eAssuming you\u0026rsquo;re using \u003ccode\u003evim-plug\u003c/code\u003e you might come to the point where you want to uninstall a plugin. Googling around will land you at \u003ca href=\"https://github.com/junegunn/vim-plug/issues/121\"\u003ea GitHub issue\u003c/a\u003e where a contributor named Andrea Cedraro states a workaround instead.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eDelete Plug line from \u003ccode\u003e.vimrc\u003c/code\u003e or \u003ccode\u003e~/.config/nvim/init.vim\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eSource the file. e.g \u003ccode\u003e:so .vimrc\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eCall \u003ccode\u003e:PlugClean\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n",
    "date_published": "2020.03.23",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/neovimplugins/",
    "url": "https://brandonrozek.com/blog/neovimplugins/",
    "title": "Neovim Plugins",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI decided to switch to \u003ca href=\"https://neovim.io/\"\u003e\u003ccode\u003eneovim\u003c/code\u003e\u003c/a\u003e recently and that resulted in having to redo my setup. This post will describe how to setup plugins with \u003ccode\u003evim-plug\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFirst install neovim\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install neovim\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNext, setup the \u003ccode\u003evim-plug\u003c/code\u003e manager\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  -fLo ~/.local/share/nvim/site/autoload/plug.vim \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen, you can load your favorite plugins inside of \u003ccode\u003e~/.config/nvim/init.vim\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-vim\" data-lang=\"vim\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u0026#34;\u0026#34; Plugins Section\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ecall\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eplug\u003c/span\u003e#\u003cspan style=\"color:#a6e22e\"\u003ebegin\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePlug\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;airblade/vim-gitgutter\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34; Git Integration\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePlug\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;junegunn/fzf\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34; Fuzzy Finder\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePlug\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;vim-airline/vim-airline\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34; Status Line\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePlug\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;dense-analysis/ale\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34; Async Lint\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePlug\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;scrooloose/nerdtree\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e\u0026#34; Directory Tree\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ecall\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eplug\u003c/span\u003e#\u003cspan style=\"color:#a6e22e\"\u003eend\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003emap\u003c/span\u003e \u0026lt;\u003cspan style=\"color:#a6e22e\"\u003eC\u003c/span\u003e-\u003cspan style=\"color:#a6e22e\"\u003eo\u003c/span\u003e\u0026gt; :\u003cspan style=\"color:#a6e22e\"\u003eNERDTreeToggle\u003c/span\u003e\u0026lt;\u003cspan style=\"color:#a6e22e\"\u003eCR\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFinally, enter \u003ccode\u003evim\u003c/code\u003e and type \u003ccode\u003e:PlugInstall\u003c/code\u003e to install all the plugins listed.\u003c/p\u003e\n",
    "date_published": "2020.03.23",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/sphinxmathjax/",
    "url": "https://brandonrozek.com/blog/sphinxmathjax/",
    "title": "Sphinx \u0026 MathJax",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eTo include mathematical notation in Python docs generated by Sphinx, it should look like the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eLine\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003er\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    Holds the equation for a line.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    :math:`y = mx + \\frac\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{b}{1}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e`\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    In this equation, :math:`m` is the slope and :math:`b` is the y-intercept.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003epass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe content after \u003ccode\u003e:math:\u003c/code\u003e and inside the backticks is treated as inline math. The \u003ccode\u003er\u003c/code\u003e in front of the triple quotes is used to make it a raw string and so that LaTex commands such as \u003ccode\u003e\\frac{}{}\u003c/code\u003e operate as intended. There also cannot be a space between \u003ccode\u003e:math:\u003c/code\u003e and the backtick.\u003c/p\u003e\n\u003cp\u003eThe Sphinx configuration needs to have the \u003ccode\u003emathjax\u003c/code\u003e extension loaded as well.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eextensions \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# ....\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;sphinx.ext.mathjax\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# ....\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.03.20",
    "tags": ["Documentation","Python","LaTex"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/gitpatch/",
    "url": "https://brandonrozek.com/blog/gitpatch/",
    "title": "Sharing Patches in Git",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe Linux kernel community make use of patches in git to share code changes with one another. Patches are only nicely formatted differences between your current codebase and what you compare it to. If you want to share a subsetted git tree, then \u003ca href=\"/blog/gitbundle/\"\u003egit bundle\u003c/a\u003e would be the way to go.\u003c/p\u003e\n\u003ch2 id=\"creating-the-patch\"\u003eCreating the patch\u003c/h2\u003e\n\u003cp\u003eIf you want to create a patch based off the difference between your current branch and master, run the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit format-patch master --stdout \u0026gt; feature_branch.patch\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can then email the patch to the team.\u003c/p\u003e\n\u003ch2 id=\"applying-the-patch\"\u003eApplying the patch\u003c/h2\u003e\n\u003cp\u003eTo apply go into a new branch and apply the patch file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit checkout -b feature_branch\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit apply /path/to/feature_branch.patch\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.03.20",
    "tags": ["Git"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/gitbundle/",
    "url": "https://brandonrozek.com/blog/gitbundle/",
    "title": "Sharing Specific Commits with Git Bundle",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIf you have a large software repository, sometimes you only want to share part of it with a group. You can accomplish this by using \u003ccode\u003egit bundle\u003c/code\u003e\u003c/p\u003e\n\u003ch2 id=\"creating-the-bundle\"\u003eCreating the Bundle\u003c/h2\u003e\n\u003cp\u003eTo bundle all the commits from the development branch to the current head,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit bundle create repo.bundle development..HEAD feature_branch\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will place these commits into a branch called \u003ccode\u003efeature_branch\u003c/code\u003e in \u003ccode\u003erepo.bundle\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"fetching-from-bundle\"\u003eFetching from bundle\u003c/h2\u003e\n\u003cp\u003eOn the other side, we need to make sure that we have all the commits up to the \u003ccode\u003edevelopment\u003c/code\u003e branch synchronized. Then we can fetch the commits from the bundle:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit fetch /path/to/repo.bundle feature_branch:feature_branch\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe left side of the colon is what you want to grab from the bundle, the right side is the branch to put the commits to.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egit checkout feature_branch\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.03.20",
    "tags": ["Git"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/qtcpsocket/",
    "url": "https://brandonrozek.com/blog/qtcpsocket/",
    "title": "QTcpSocket",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThere are two ways that I can think of for checking if a TCP socket times out in Qt. You can either use \u003ccode\u003ewaitForConnected\u003c/code\u003e or a \u003ccode\u003eQTimer\u003c/code\u003e.  The Qt 5.14 documentation noted that the \u003ccode\u003ewaitForConnected\u003c/code\u003e call may randomly fail in Windows.\u003c/p\u003e\n\u003cp\u003eHere is some shared code for both examples\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eQTcpSocket \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003esocket \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e QTcpSocket(\u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003equint16 listenPort \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e4444\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e timeout \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1000\u003c/span\u003e; \u003cspan style=\"color:#75715e\"\u003e// Units: milliseconds\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003eQHostAddress \u003cspan style=\"color:#a6e22e\"\u003edestination\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;192.168.0.2\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"waitfordisconnected\"\u003e\u003ccode\u003ewaitForDisconnected\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s say that we have a \u003ccode\u003eQTcpScoket\u003c/code\u003e pointer named \u003ccode\u003esocket\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esocket\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003econnectToHost(destination, listenPort);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e!\u003c/span\u003esocket\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003ewaitForConnected(timeout)) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    qDebug(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Connection Timed Out.\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNotes:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003ewaitForConnected\u003c/code\u003e is a blocking call\u003c/li\u003e\n\u003cli\u003eThis does not account for the host lookup call.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"qtimer\"\u003e\u003ccode\u003eQTimer\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eThis method requires a little more setup. Let\u0026rsquo;s assume we have a class named \u003ccode\u003eTest\u003c/code\u003e that inherits \u003ccode\u003eQObject\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// ....\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003esocket\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003econnectToHost(destination, listenPort);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etimeoutTimer \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e QTimer(\u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etimeoutTimer\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003esetInterval(timeout);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etimeoutTimer\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003esetSingleShot(true);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econnect(timeoutTimer, \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003eQTimer\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003etimeout, \u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e, \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003eTest\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003etimeout);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etimeoutTimer\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003estart();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// ....\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e Test\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003etimeout(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    qDebug(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Connected Timed Out.\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    socket\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003edisconnectFromHost();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNotes:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThis method acts asynchronously\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIn order for the the timeout function to not always hit, we need to make sure we stop the timer when data is received or a TCP error occurs\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etimeoutTimer\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003estop();\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.03.20",
    "tags": ["C++"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/qttimer/",
    "url": "https://brandonrozek.com/blog/qttimer/",
    "title": "Qt Timers",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eQt has two great timers, one that repeats an action after a certain interval, and one that is meant for one-off operations. They call these \u003ccode\u003eQTimer\u003c/code\u003e and \u003ccode\u003eQTimer::singleShot\u003c/code\u003e respectively. This post is going to assume that we\u0026rsquo;re working with a class named \u003ccode\u003eTest\u003c/code\u003e that inherits \u003ccode\u003eQObject\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eLet us first look at the one that repeats. This code needs to be inside a class that inherits \u003ccode\u003eQObject\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e Test\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003ecallbackRepeat(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Code that executes when the timer times out\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// ......\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e interval \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1000\u003c/span\u003e; \u003cspan style=\"color:#75715e\"\u003e// Units: milliseconds\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003eQTimer\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e timer \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e QTimer(\u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etimer\u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003estart(interval);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econnect(timer, \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003eQTimer\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003etimeout, \u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e, \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003eTest\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003ecallbackRepeat);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow for the one-off\u0026hellip;\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e Test\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003ecallback(\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e// Code that executes when the timer times out\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// ......\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e timeout \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1000\u003c/span\u003e; \u003cspan style=\"color:#75715e\"\u003e// Units: milliseconds\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003eQTimer\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003esingleShot(timeout, \u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e, \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003eTest\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003ecallback)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.03.19",
    "tags": ["C++"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pythonoverloads/",
    "url": "https://brandonrozek.com/blog/pythonoverloads/",
    "title": "Python Operator Overloads",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI wrote a \u003ca href=\"/blog/cppoverloads/\"\u003eblog post about operator overloads\u003c/a\u003e in C++. Luckily for Python it is heavily document in what is called the \u003ca href=\"https://docs.python.org/3/reference/datamodel.html\"\u003ePython Data Model\u003c/a\u003e. Though for the sake of having content, I\u0026rsquo;ll share some of the ones that I heavily use in my classes.\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eOperator\u003c/th\u003e\n          \u003cth\u003eMethod\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e+\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e__add__(self, other)\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e==\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e__eq__(self, other)\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003elen()\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e__len__(self)\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003estr()\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e__str__(self)\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003ehash()\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e__hash__(self)\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eExample Usage:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eTest\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __init__(self, x):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ex \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e x\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __add__(self, other):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e Test(self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ex, other\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ex)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __eq__(self, other):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ex \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e other\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ex\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __len__(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e len(self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ex)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __str__(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ex\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __hash__(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e hash(self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ex)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.03.13",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/cppoverloads/",
    "url": "https://brandonrozek.com/blog/cppoverloads/",
    "title": "C++ Overloads",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOne thing I like about a lot of common languages like C++ and Python is that you can overload operators. This makes it easy to create libraries for other developers that are easy to use. We are going to explain common operators you may want to overload in C++.\u003c/p\u003e\n\u003ch2 id=\"quick-example\"\u003eQuick Example\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s create a class\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDataType\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      DataType() {}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#66d9ef\"\u003ebool\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eprivate\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e y;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNotice that we\u0026rsquo;re going to overload the \u003ccode\u003e==\u003c/code\u003e operator for this DataType.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ebool\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003ex1, \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003ex2) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e x1.y \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e x2.y;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"common-unary-operators\"\u003eCommon Unary Operators\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eName\u003c/th\u003e\n          \u003cth\u003eOperator\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eIncrement\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e++\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eDecrement\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e--\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eFunction Call\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e()\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eBoolean Conversion\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e(bool)\u003c/code\u003e object\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDataType\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DataType() {}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Boolean Conversion\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    \u003cspan style=\"color:#66d9ef\"\u003ebool\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ebool\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// (In/De)crements\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e(DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e--\u003c/span\u003e(DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Function Call operator\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eoperator\u003c/span\u003e(DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e x)(\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eprivate\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e y;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Boolean Conversion\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003ebool\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ebool\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e x) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e x.y \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// (In/De)crements\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003eDataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e(DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e x) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    x.y\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e x;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e--\u003c/span\u003e(DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e x) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    x.y\u003cspan style=\"color:#f92672\"\u003e--\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e x;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Function Call Operator\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eoperator\u003c/span\u003e(DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e x)(\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e n) { \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    x.y \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e n; \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"common-binary-operators\"\u003eCommon Binary Operators\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eName\u003c/th\u003e\n          \u003cth\u003eOperator\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eStream Insertion\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e\u0026lt;\u0026lt;\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eStream Extraction\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e\u0026gt;\u0026gt;\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eLess than\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e\u0026lt;\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eEquals\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e==\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eSubscript\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e[]\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eImplementing \u003ccode\u003e\u0026lt;\u003c/code\u003e gives you \u003ccode\u003e\u0026gt;\u003c/code\u003e,\u003ccode\u003e\u0026lt;=\u003c/code\u003e, and \u003ccode\u003e\u0026gt;=\u003c/code\u003e for free.\u003c/p\u003e\n\u003cp\u003eImplementing \u003ccode\u003e==\u003c/code\u003e gives you \u003ccode\u003e!=\u003c/code\u003e for free.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDataType\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DataType() {}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DataType(\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e x) { y \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e x }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Stream Operators\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eostream\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e(std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eostream\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eistream\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u0026gt;\u003c/span\u003e(std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eistream\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e, DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Arithmetic Operators\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e(DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    DataType \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Comparison Operators\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    \u003cspan style=\"color:#66d9ef\"\u003ebool\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ebool\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Array Subscript\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e[](DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e, std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003esize_t);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eprivate\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e y;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-c++\" data-lang=\"c++\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Stream Operators\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003estd\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eostream\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e(std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eostream\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e os, \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e x) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    os \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e x.y;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e os;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003estd\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eistream\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u0026gt;\u003c/span\u003e(std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003eistream\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e is, DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e x) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e y;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    is \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u0026gt;\u003c/span\u003e y;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    x.y \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e y;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e is;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Artithmetic Operators\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003eDataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e(DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e x, \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e z) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    x.y \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e z.y;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e x;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDataType \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e x, \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e z) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDataType\u003c/span\u003e(x.y \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e z.y);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Comparison Operators\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003ebool\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003ex, \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003ez) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e x.y \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e z.y;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ebool\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003ex, \u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e DataType \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003ez) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e x.y \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e z.y;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Array Subscript\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eoperator\u003c/span\u003e[](DataType\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e x, std\u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003esize_t idx) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e x.y;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.03.07",
    "tags": ["C++"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/recordoutputaudio/",
    "url": "https://brandonrozek.com/blog/recordoutputaudio/",
    "title": "Record Output Audio via Terminal",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis post is specific to \u003ccode\u003ePulseAudio\u003c/code\u003e on Linux.\u003c/p\u003e\n\u003cp\u003eI know of GUI based solutions like \u003ca href=\"https://freedesktop.org/software/pulseaudio/pavucontrol/\"\u003ePulseAudio Volume Control\u003c/a\u003e that lets you set up monitor devices. But, what if you want to do this through the terminal?\u003c/p\u003e\n\u003cp\u003eLuckily, \u003ca href=\"https://askubuntu.com/a/850174\"\u003eb-ak\u003c/a\u003e on AskUbuntu gave an elegant answer to this question!\u003c/p\u003e\n\u003cp\u003eFirst make sure you have \u003ccode\u003epulseaudio-utils\u003c/code\u003e installed,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install pulseaudio-utils\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNext we need to search for the speaker we wish to monitor\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epacmd list-sinks | grep -e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;name:\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e                        -e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;index\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e                        -e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Speakers\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIt will output something similar to this:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  * index: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        name: \u0026lt;alsa_output.pci-0000_00_1b.0.analog-stereo\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                analog-output-speaker: Speakers \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003epriority 10000, latency offset \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e usec, available: unknown\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFrom here note the name in \u003ccode\u003e\u0026lt;\u0026gt;\u003c/code\u003e of the speaker you wish to monitor. For example for my output above, I wish to monitor \u003ccode\u003ealsa_output.pci-0000_00_1b.0.analog-stereo\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eNext we will use the \u003ccode\u003eparec\u003c/code\u003e command to record the raw audio stream from the PulseAudio server.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eparec --device alsa_output.pci-0000_00_1b.0.analog-stereo.monitor | encoder_command\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNotice the addition of \u003ccode\u003e.monitor\u003c/code\u003e at the end of the device.\u003c/p\u003e\n\u003ch2 id=\"lame\"\u003e\u003ccode\u003elame\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eFor the \u003ccode\u003eencoder_command\u003c/code\u003e, b-ak used \u003ccode\u003elame\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elame -r -V0 - out.mp3\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis command takes in a raw pcm \u003ccode\u003e-r\u003c/code\u003e and enables variable bit rates for the highest quality \u003ccode\u003e-V0\u003c/code\u003e. From there it encodes it and puts it in \u003ccode\u003eout.mp3\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eNow \u003ccode\u003elame\u003c/code\u003e actually makes a couple assumptions about your raw pcm if you didn\u0026rsquo;t specify additional arguments:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThe Raw PCM is formatted in signed 16-bit little endian samples\u003c/li\u003e\n\u003cli\u003eThe Raw PCM has 2 channels\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIf you\u0026rsquo;re assumptions don\u0026rsquo;t meet the above, then you will need to add additional arguments.\u003c/p\u003e\n\u003ch2 id=\"ffmpeg\"\u003e\u003ccode\u003effmpeg\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eWe can replace \u003ccode\u003elame\u003c/code\u003e with the more featureful \u003ccode\u003effmpeg\u003c/code\u003e if we take note of the same assumptions above.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003effmpeg -f s16le \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e       -ac \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e       -i pipe:0 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e       -b:a \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e       out.mp3\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhere we can replace the \u003ccode\u003e.mp3\u003c/code\u003e with whatever file extension \u003ccode\u003effmpeg\u003c/code\u003e supports.\u003c/p\u003e\n\u003cp\u003eNow to show the entire command\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eparec --device alsa_output.pci-0000_00_1b.0.analog-stereo.monitor | \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  ffmpeg -f s16le \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e         -ac \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e         -i pipe:0 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e         -b:a \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e         out.mp3\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.03.01",
    "tags": ["Linux","Audio-Video"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/espeak/",
    "url": "https://brandonrozek.com/blog/espeak/",
    "title": "Text to Speech on Linux with Espeak",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ccode\u003eespeak\u003c/code\u003e is a command line tool that lets you type in messages and have it said back to you.\u003c/p\u003e\n\u003cp\u003eTo install on Ubuntu\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install espeak\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIt\u0026rsquo;s as simple as running it and typing out what you want to say\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20200301113507984.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003caudio controls src=\"/files/audio/20200301113507984.mp3\"\u003e\n\u003c/audio\u003e\n\u003cp\u003e\u003ca href=\"https://delightlylinux.wordpress.com/2015/03/23/linux-has-voice-with-espeak/\"\u003eDelightly Linux\u003c/a\u003e wrote a great post describing the different features \u003ccode\u003eespeak\u003c/code\u003e has.\u003c/p\u003e\n\u003cp\u003ePlaying around with different voices and I can get something like this:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20200301115220550.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003caudio controls src=\"/files/audio/20200301115220550.mp3\"\u003e\n\u003c/audio\u003e\n\u003cp\u003eYou can also replicate the sound above by piping the text into \u003ccode\u003eespeak\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Warning warning the build has failed\u0026#34;\u003c/span\u003e | espeak -s \u003cspan style=\"color:#ae81ff\"\u003e140\u003c/span\u003e -v en+f4\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"subset-of-arguments\"\u003eSubset of Arguments\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eArgument\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e-f\u003c/td\u003e\n          \u003ctd\u003eText file to speak\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e-p\u003c/td\u003e\n          \u003ctd\u003ePitch adjustment from 0 to 99 (default: 50)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e-s\u003c/td\u003e\n          \u003ctd\u003eSpeed in words per minute (default: 160)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e-v\u003c/td\u003e\n          \u003ctd\u003eVoice file from \u003ccode\u003eespeak-data/voices\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e-w\u003c/td\u003e\n          \u003ctd\u003eWrite output to WAV file instead of speakers\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n",
    "date_published": "2020.03.01",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/dbcli/",
    "url": "https://brandonrozek.com/blog/dbcli/",
    "title": "Dbcli",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe \u003ca href=\"https://www.dbcli.com/\"\u003eDBLCI\u003c/a\u003e project creates command line database clients with auto-completion and syntax highlighting. These clients are often nicer to work with than the one a database comes with. In this post we\u0026rsquo;re going to demo the \u003ca href=\"https://litecli.com/\"\u003eLiteCLI\u003c/a\u003e client.\u003c/p\u003e\n\u003cp\u003eFirst to install,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install litecli\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI created a sqlite database and populated it with the following code\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-sqlite\" data-lang=\"sqlite\"\u003e.open test.db\n\nCREATE TABLE Users (\n  UserID INTEGER PRIMARY KEY,\n  firstName TEXT NOT NULL,\n  lastName TEXT NOT NULL\n);\n\nINSERT INTO Users (firstName, lastName) VALUES (\u0026#34;Pearl\u0026#34;, \u0026#34;Perez\u0026#34;);\nINSERT INTO Users (firstName, lastName) VALUES (\u0026#34;Derrick\u0026#34;, \u0026#34;Norton\u0026#34;);\nINSERT INTO Users (firstName, lastName) VALUES (\u0026#34;Joan\u0026#34;, \u0026#34;Daniels\u0026#34;);\nINSERT INTO Users (firstName, lastName) VALUES (\u0026#34;Marian\u0026#34;, \u0026#34;Lane\u0026#34;);\nINSERT INTO Users (firstName, lastName) VALUES (\u0026#34;Roland\u0026#34;, \u0026#34;Gregory\u0026#34;);\nINSERT INTO Users (firstName, lastName) VALUES (\u0026#34;Charlene\u0026#34;, \u0026#34;Baldwin\u0026#34;);\nINSERT INTO Users (firstName, lastName) VALUES (\u0026#34;Lester\u0026#34;, \u0026#34;Kennedy\u0026#34;);\nINSERT INTO Users (firstName, lastName) VALUES (\u0026#34;Dan\u0026#34;, \u0026#34;Vasquez\u0026#34;);\nINSERT INTO Users (firstName, lastName) VALUES (\u0026#34;Genevieve\u0026#34;, \u0026#34;Dean\u0026#34;);\nINSERT INTO Users (firstName, lastName) VALUES (\u0026#34;Sue\u0026#34;, \u0026#34;Bennett\u0026#34;);\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNow let\u0026rsquo;s open it up using \u003ccode\u003elitecli\u003c/code\u003e and show the syntax highlighting.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20200229185045757.png\" alt=\"image-20200229185045757\"\u003e\u003c/p\u003e\n",
    "date_published": "2020.02.29",
    "tags": ["Python","DB"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/resumingapt/",
    "url": "https://brandonrozek.com/blog/resumingapt/",
    "title": "Resuming Stopped Apt Updates",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eEspecially on the Raspberry Pi, it is quite often for me to lose connection while updating. I know some people setup \u003ccode\u003escreen\u003c/code\u003e or \u003ccode\u003etmux\u003c/code\u003e sessions so that they can easily reconnect. I\u0026rsquo;m not good at remembering to do this. \u003ca href=\"https://unix.stackexchange.com/a/46546\"\u003eAngus and Cas\u003c/a\u003e on StackExchange shared that to resume a stopped update\u0026hellip;\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo kill \u003cspan style=\"color:#66d9ef\"\u003e$(\u003c/span\u003epgrep dpkg\u003cspan style=\"color:#66d9ef\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dpkg --configure --pending\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install -f\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhich will then configure the packages that have already been installed but not configured yet and finish installing packages.\u003c/p\u003e\n",
    "date_published": "2020.02.29",
    "tags": ["Ubuntu","Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pythonsetinterval/",
    "url": "https://brandonrozek.com/blog/pythonsetinterval/",
    "title": "Python: Set Interval",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eJavascript has a function called \u003ccode\u003esetInterval\u003c/code\u003e which given a length of time $T$ and a callback function, it will perform that function every $T$ milliseconds. For example, to print \u0026ldquo;Hello, World!\u0026rdquo; every 5 seconds:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003esetInterval\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Hello, World!\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}, \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1000\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWouldn\u0026rsquo;t it be nice if Python had a similar functionality? Well thanks to \u003ca href=\"https://stackoverflow.com/a/48741004\"\u003eright2clicky\u003c/a\u003e, there\u0026rsquo;s a nice and quick way to implement one.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e threading \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e Timer\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eRepeat\u003c/span\u003e(Timer):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erun\u003c/span\u003e(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efinished\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ewait(self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003einterval):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efunction(\u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003eself\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eargs, \u003cspan style=\"color:#f92672\"\u003e**\u003c/span\u003eself\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ekwargs)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSince \u003ccode\u003eself.finished.wait\u003c/code\u003e only returns \u003ccode\u003eTrue\u003c/code\u003e when the Event \u003ccode\u003eself.finished\u003c/code\u003e is set to true, the thread will keep waiting and calling the function for the set interval time period.\u003c/p\u003e\n\u003cp\u003eThe same post has a usage example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e time \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e sleep\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003et \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Repeat(\u003cspan style=\"color:#ae81ff\"\u003e1.0\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003elambda\u003c/span\u003e: print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Hello, World!\u0026#34;\u003c/span\u003e))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003et\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003estart()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esleep(\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003et\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ecancel()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.02.25",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pythonsetupdevelop/",
    "url": "https://brandonrozek.com/blog/pythonsetupdevelop/",
    "title": "Python setup.py develop",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cstrong\u003eDeprecated in favor of \u003ca href=\"/blog/pipeditable/\"\u003epip install editable\u003c/a\u003e\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve found it to be incredibly helpful to emulate having a library installed on my system rather than depending on my local directory path to pick up my file edits. To do this in a python project where you\u0026rsquo;ve defined a \u003ccode\u003esetup.py\u003c/code\u003e, you can specify the command \u003ccode\u003edevelop\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFirst uninstall whatever version of your \u003ccode\u003elibrary\u003c/code\u003e you have.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip uninstall library\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen in your folder with the \u003ccode\u003esetup.py\u003c/code\u003e run the following command\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epython setup.py develop\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will then create a symlink from your site-packages directory to the directory in which your code lives.\u003c/p\u003e\n\u003cp\u003eOnce you\u0026rsquo;re ready to install it formally,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip uninstall library\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install .\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDistribute it,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip wheel .\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.02.21",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/tox/",
    "url": "https://brandonrozek.com/blog/tox/",
    "title": "Automate Python Test Suites with Tox",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://tox.readthedocs.io/en/latest/\"\u003eTox\u003c/a\u003e is a great project where you can automate your testing using virtual environments.\u003c/p\u003e\n\u003cp\u003eFirst install tox\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install tox\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI like to write my tests in Python\u0026rsquo;s native \u003ca href=\"https://docs.python.org/3/library/unittest.html\"\u003e\u003ccode\u003eunittest\u003c/code\u003e\u003c/a\u003e format. Tests should be stored in a \u003ccode\u003etests\u003c/code\u003e directory.\u003c/p\u003e\n\u003cp\u003eI then combine it with the \u003ccode\u003ecoverage\u003c/code\u003e library to tell me how much of my code that my test cases cover. To quickly insert my personal opinion, I never aim for 100% test coverage since there\u0026rsquo;s typically overhead in maintaining that.\u003c/p\u003e\n\u003cp\u003eThis all gets described in a \u003ccode\u003etox.ini\u003c/code\u003e file. This file should live in the same directory as your \u003ccode\u003esetup.py\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[tox]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eenvlist\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    py38\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[testenv]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003edeps\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ecoverage\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ecommands\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    coverage run --source=tests,library -m unittest discover tests\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    coverage report -m\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.02.21",
    "tags": ["Python","Testing"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pyenvtox/",
    "url": "https://brandonrozek.com/blog/pyenvtox/",
    "title": "Testing your Python Application on Multiple Versions with Pyenv and Tox",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003ePyenv is great for managing multiple python installations and tox is great for creating virtual environments for testing. What if we can combine the two? For more detailed information visit \u003ca href=\"https://blog.frank-mich.com/recipe-testing-multiple-python-versions-with-pyenv-and-tox/\"\u003eFrank-Mich\u0026rsquo;s Blog\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eFirst make sure \u003ca href=\"https://github.com/pyenv/pyenv-installer\"\u003epyenv is installed\u003c/a\u003e. In the directory with your \u003ccode\u003esetup.py\u003c/code\u003e tell \u003ccode\u003epyenv\u003c/code\u003e which python versions you want to consider.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv local 3.6.0 3.7.0 3.8.0\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFrank warns heavily not to specify multiple python versions with the same \u003ccode\u003emajor.minor\u003c/code\u003e numbering. For example, \u003ccode\u003e3.6.0\u003c/code\u003e and \u003ccode\u003e3.6.10\u003c/code\u003e should not be included together.\u003c/p\u003e\n\u003cp\u003eThen install the \u003ccode\u003etox\u003c/code\u003e package.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install tox\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI made the mistake of making a virtual environment and then installing tox. That gets rid of the python version information we specified before.\u003c/p\u003e\n\u003cp\u003eNow specify a \u003ccode\u003etox.ini\u003c/code\u003e with a structure similar to below..\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[tox]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eenvlist\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    py36\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    py37\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    py38\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[testenv]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ecommands\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    python3 -m unittest discover tests\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.02.21",
    "tags": ["Python","Testing"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/youtubebackup/",
    "url": "https://brandonrozek.com/blog/youtubebackup/",
    "title": "Backing Up YouTube Content",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThere are great content on YouTube that I would be sad if it went away. Therefore, I did some digging around and found a \u003ca href=\"https://www.reddit.com/r/DataHoarder/comments/863aid/what_is_your_method_of_viewing_youtubedl_backed/dw25vnm/\"\u003egreat discussion\u003c/a\u003e on Reddit on backing up YouTube videos. The solution is based on \u003ccode\u003eyoutube-dl\u003c/code\u003e and I modified the script a little to fit my needs.\u003c/p\u003e\n\u003cp\u003eThe options added to \u003ccode\u003eyoutube-dl\u003c/code\u003e makes it so that meta information such as subtitles, thumbnails, etc get added to the video.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eyoutube-dl --ignore-errors \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e           --playlist-reverse \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e           --output \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;%(uploader)s/%(uploader)s - %(title)s - %(upload_date)s.%(ext)s\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e           --format \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;bestvideo[ext=mp4]+bestaudio[ext=m4a]\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e           --merge-output-format mp4 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e           --all-subs \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e           --embed-subs \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e           --embed-thumbnail \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e           --add-metadata \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e           URL_HERE\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"audio-only\"\u003eAudio Only\u003c/h2\u003e\n\u003cp\u003eTo extract only audio here\u0026rsquo;s the modified command\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eyoutube-dl --ignore-errors \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e           --playlist-reverse \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e           --output \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;%(uploader)s/%(uploader)s - %(title)s - %(upload_date)s.%(ext)s\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e           --format \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;bestvideo[ext=mp4]+bestaudio[ext=m4a]\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e           --merge-output-format mp4 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e           --embed-thumbnail \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e           --add-metadata \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e           --extract-audio\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e           URL_HERE\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.02.17",
    "tags": ["Archive"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/recursivelisting/",
    "url": "https://brandonrozek.com/blog/recursivelisting/",
    "title": "Quick Python: List Files Recursively",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn order to add website files into a Flask application using setuptools, I needed to recurse down several directories and grab HTML and CSS files.\u003c/p\u003e\n\u003cp\u003eThis Python tip will show you how to get a list of files recursively.\u003c/p\u003e\n\u003cp\u003eFirst in order to solve this problem, we\u0026rsquo;re going to recursively get a list of directories.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e glob \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e glob\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edirectories \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e glob(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/path/to/directory/**/\u0026#39;\u003c/span\u003e, recursive\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eTrue\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow we will go in each directory and grab the files,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edata_files \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e directory \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e directories:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    data_files \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e list(filter(\u003cspan style=\"color:#66d9ef\"\u003elambda\u003c/span\u003e x: x \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/\u0026#39;\u003c/span\u003e  \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e directories, glob(directory \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;*\u0026#39;\u003c/span\u003e)))\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor setuptools, you would want the \u003ccode\u003edata_files\u003c/code\u003e variable to be a list of tuples with module names and the files associated with them.\u003c/p\u003e\n\u003cp\u003eComplete script:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edata_files \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edirectories \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e glob(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;path/to/files/**/\u0026#39;\u003c/span\u003e, recursive\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eTrue\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e directory \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e directories:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    files \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e list(filter(\u003cspan style=\"color:#66d9ef\"\u003elambda\u003c/span\u003e x: x \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e directories, glob(directory \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;*\u0026#34;\u003c/span\u003e)))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    data_files\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend((directory, files))\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor setuptools don\u0026rsquo;t forget to \u003ccode\u003einclude_package_data\u003c/code\u003e!\u003c/p\u003e\n",
    "date_published": "2020.02.09",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/gstreamer/",
    "url": "https://brandonrozek.com/blog/gstreamer/",
    "title": "GStreamer",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eGStreamer is a pipeline based multimedia framework that goes from capture, processing, to a sink such as a X window or UDP sink.\u003c/p\u003e\n\u003cp\u003eTo install on Ubuntu run,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install gstreamer1.0-tools\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003ePipelines are indicated via \u003ccode\u003e!\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eTo capture your webcam feed and display it in a X window, run the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egst-launch-1.0 v4l2src device\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/dev/video0 ! videoconvert ! ximagesink\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor the PiCar project I\u0026rsquo;m on, I need to send the video from the raspberry pi over the network as fast as possible. The next couple commands have options that reflect this.\u003c/p\u003e\n\u003cp\u003eOn the PiCar, I send h264 video over the network. You might need to install an additional package for h264 encoding to work.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install gstreamer1.0-plugins-ugly\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo send h264 video over the network,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egst-launch-1.0 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  v4l2src device\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/dev/video0 ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  videoconvert ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  x264enc ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  rtph264pay ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  udpsink host\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e192.168.0.2 port\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e5000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo send it as fast as you can,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egst-launch-1.0 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  v4l2src device\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/dev/video0 ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  videoconvert ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  x264enc interlaced\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003etrue tune\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ezerolatency speed-preset\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eultrafast ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  rtph264pay ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  udpsink host\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e192.168.0.2 port\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e5000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo send it as fast as possible under a given framerate (ex: 30)\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egst-launch-1.0 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  v4l2src device\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/dev/video0 ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  videoconvert ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  videorate ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  video/x-raw,framerate\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e30/1 ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  x264enc interlaced\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003etrue tune\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ezerolatency speed-preset\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eultrafast ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  rtph264pay ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  udpsink host\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e192.168.0.2 port\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e5000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo receive it and display it on an X-window,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egst-launch-1.0 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  udpsrc port\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e5000\u003c/span\u003e ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  application/x-rtp,payload\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e96\u003c/span\u003e ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  rtph264depay ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  decodebin ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  videoconvert n-threads\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e ! \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  ximagesink\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.02.08",
    "tags": ["Audio-Video"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/missinglibraries/",
    "url": "https://brandonrozek.com/blog/missinglibraries/",
    "title": "Missing Libraries",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe piwheels blog outlined a great \u003ca href=\"https://blog.piwheels.org/how-to-work-out-the-missing-dependencies-for-a-python-package/\"\u003epost\u003c/a\u003e on what to do when you are missing shared libraries in Python packages. Though the short of this tip is helpful on its own as well. If you are running any piece of software and its missing a library, try to find the \u003ccode\u003e.so\u003c/code\u003e file related to that package.\u003c/p\u003e\n\u003cp\u003eThen run \u003ccode\u003eldd\u003c/code\u003e on that file passing it to grep to filter for not found packages:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eldd file.so | grep \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;not found\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd if you\u0026rsquo;re on Ubuntu, then you can use \u003ccode\u003eapt-file\u003c/code\u003e to find the related packages.\u003c/p\u003e\n",
    "date_published": "2020.02.08",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pythonpackagenamespacing/",
    "url": "https://brandonrozek.com/blog/pythonpackagenamespacing/",
    "title": "Quick Python: Package Namespacing",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003ePackage namespacing can help organize modules within a larger project. It can also help show that a package belongs to an organization.\u003c/p\u003e\n\u003cp\u003eFor example, let\u0026rsquo;s say you want to have a module named \u003ccode\u003ecompanyname.component\u003c/code\u003e. Then you want to have the following project structure.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ecompanyname/\n  component/\n    __init__.py\n    ...\nsetup.py\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eNote that there is no \u003ccode\u003e__init__.py\u003c/code\u003e under \u003ccode\u003ecompanyname\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThen in your \u003ccode\u003esetup.py\u003c/code\u003e denote where to find the packages using a namespace.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e setuptools \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e setup, find_namespace_packages\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esetup(name\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;companyname.component\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e     version\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;0.0.1\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e     packages\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003efind_namespace_packages(include\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e[\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;companyname.*\u0026#34;\u003c/span\u003e]))\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.02.03",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/customexec/",
    "url": "https://brandonrozek.com/blog/customexec/",
    "title": "Custom Executables",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eAs time goes on more and more scripts start to accumulate in my scripts folder. It turns out though, that there is a standard way of having local user scripts that get automatically added to your path. I started noticing this in Ubuntu when more and more projects started utilizing the \u003ccode\u003e~/.local/bin\u003c/code\u003e directory.\u003c/p\u003e\n\u003cp\u003eSo stick any scripts you have in there since by default on some distributions \u003ccode\u003e$PATH\u003c/code\u003e is setup to look there!\u003c/p\u003e\n",
    "date_published": "2020.02.03",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/sshjump/",
    "url": "https://brandonrozek.com/blog/sshjump/",
    "title": "SSH Jump",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWith ssh jump, we can ssh into a machine that we don\u0026rsquo;t have direct access to through an intermediary.\u003c/p\u003e\n\u003cp\u003eJust chain the necessary hosts together with the following command:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003essh -J host1 host2 host3 ...\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhere the last host is the one you wish to connect to at the end.\u003c/p\u003e\n\u003cp\u003eLuckily ssh config also supports this!\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eHost host1\n  HostName host1.example.com\n\nHost host2\n  HostName hidden.host1.example.com\n  ProxyJump host1\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2020.02.02",
    "tags": ["SSH"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pingdiscovery/",
    "url": "https://brandonrozek.com/blog/pingdiscovery/",
    "title": "Find Devices on your Network with a Ping Scan",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003ePlugging in a device into a network with DHCP will often result in you not knowing what the ip is. If you don\u0026rsquo;t have easy access to the DHCP server, then one way to see what ip addresses are on the network is to do a ping scan.\u003c/p\u003e\n\u003cp\u003ePlease make sure that this is either your home network or that you have permission before doing a scan.\u003c/p\u003e\n\u003cp\u003eFirst you\u0026rsquo;ll need to know the subnet that you are working off of.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eip addr show\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou might get something like the following\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e2: wlan0: \u0026lt;BROADCAST,MULTICAST,UP\u0026gt; mtu 1500 qdisc noqueue state UP group default qlen 1000\n    link/ether 96:25:48:13:62:02 brd ff:ff:ff:ff:ff:ff\n    inet 192.168.0.5/24 brd 192.168.0.255 scope global dynamic noprefixroute wlan0\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eRight after \u003ccode\u003einet\u003c/code\u003e note the \u003ccode\u003e192.168.0.5/24\u003c/code\u003e. The \u003ccode\u003e/24\u003c/code\u003e subnet means that the last number changes. We can replace our last number then with zero and start the ping scan.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enmap -sn 192.168.0.0/24\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.02.02",
    "tags": ["Linux","Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/bat/",
    "url": "https://brandonrozek.com/blog/bat/",
    "title": "Bat is cat with syntax highlighting",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ccode\u003ebat\u003c/code\u003e is a more human pleasing replacement of \u003ccode\u003ecat\u003c/code\u003e with the following features:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003esyntax highlighting support\u003c/li\u003e\n\u003cli\u003egit integration\u003c/li\u003e\n\u003cli\u003eautomatic paging\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eTo test it out I wrote a file called \u003ccode\u003etest.py\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e collections \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e Counter\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eitems \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e6\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ec \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Counter(items)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(c[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e])\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd here\u0026rsquo;s a screenshot of my terminal session when I called \u003ccode\u003ebat\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/20200201063050726.png\" alt=\"image-20200201063050726\"\u003e\u003c/p\u003e\n",
    "date_published": "2020.02.01",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pythonabstractclass/",
    "url": "https://brandonrozek.com/blog/pythonabstractclass/",
    "title": "Quick Python: Abstract Classes",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eYou can create an abstract class in Python by inheriting Abstract Base Class (\u003ccode\u003eABC\u003c/code\u003e) and declaring relevant methods abstract.\u003c/p\u003e\n\u003cp\u003eFirst import the following from the standard library,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e abc \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e ABC, abstractmethod\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen create an abstract class, making sure to add the decorator \u003ccode\u003e@abstractmethod\u003c/code\u003e to the constructor. This prevents the user from instantiating the class.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eAnimal\u003c/span\u003e(ABC):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003e@abstractmethod\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __init__(self, name):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e name\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003e@abstractmethod\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eis_bipedal\u003c/span\u003e(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003epass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003espeak\u003c/span\u003e(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Hello my name is \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ename \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; and I am a \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e type(self)\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e__name__\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn the example above:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eA constructor is made, however, the class Animal cannot be instantiated. Do note that child classes can call the constructor.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eis_bipedal\u003c/code\u003e is an abstract method that needs to be defined in child classes. \u003ccode\u003epass\u003c/code\u003e indicates that it is not implemented.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003espeak\u003c/code\u003e is a normal method that will be inherited by child classes.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eNow let\u0026rsquo;s look at a class that inherits \u003ccode\u003eAnimal\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eDog\u003c/span\u003e(Animal):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __init__(self, name, owner):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        super()\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e__init__(name) \u003cspan style=\"color:#75715e\"\u003e# Calls Animal constructor\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        self\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eowner \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e owner\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eis_bipedal\u003c/span\u003e(self):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eFalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.01.26",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/cloc/",
    "url": "https://brandonrozek.com/blog/cloc/",
    "title": "Count Lines of Code (cloc)",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cstrong\u003eC\u003c/strong\u003eount \u003cstrong\u003eL\u003c/strong\u003eines \u003cstrong\u003eo\u003c/strong\u003ef \u003cstrong\u003eC\u003c/strong\u003eode is an application included in the standard Ubuntu repositories that counts the lines of code separated by programming language.\u003c/p\u003e\n\u003cp\u003eIt is able to separate out blank lines, comment lines, and actual code lines in the count.  It\u0026rsquo;s quick and easy to use for giving a quick statistic on any given code repository.\u003c/p\u003e\n\u003cp\u003eTo count lines of code in the current directory,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecloc .\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAn example from my website repository:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e1019 text files.\n894 unique files.                                          \n158 files ignored.\n\ngithub.com/AlDanial/cloc v1.82  T=0.58 s (1501.1 files/s, 213974.9 lines/s)\n-----------------------------------------------------------------------\nLanguage             files          blank        comment           code\n-----------------------------------------------------------------------\nHTML                   581          14457           4041          69754\nMarkdown               248           6758              0          14620\nXML                      9           1201              0           5291\nJavaScript               8            478           1282           2623\nJSON                    11              0              0           1875\nCSS                      7            216             47           1048\nTOML                     3             22             17            120\nSVG                      1              2              0             20\nBourne Shell             1              0              0              3\n-----------------------------------------------------------------------\nSUM:                   869          23134           5387          95354\n-----------------------------------------------------------------------\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2020.01.25",
    "tags": null,
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pythoninterrupts/",
    "url": "https://brandonrozek.com/blog/pythoninterrupts/",
    "title": "Quick Python: Interrupts",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis post is part of a new series I\u0026rsquo;m starting where I quickly outline small Python snippets.\u003c/p\u003e\n\u003cp\u003eIf you have an application that you want to gracefully exit when the user presses CTRL-C here\u0026rsquo;s the short snippet\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e signal\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e sys\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Function that gets called when interrupt is found\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esignal_handler\u003c/span\u003e(sig, frame):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;You pressed Ctrl+C!\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  sys\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eexit(\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Attach the function to the interrupt signal\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esignal\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esignal(signal\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eSIGINT,signal_handler)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Press Ctrl+C\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.01.25",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/zshandsnaps/",
    "url": "https://brandonrozek.com/blog/zshandsnaps/",
    "title": "Zsh and Snaps",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn case I forget again, by default when snaps are installed it doesn\u0026rsquo;t populate in the \u003ccode\u003ezsh\u003c/code\u003e shell. To enable this add the following to \u003ccode\u003e/etc/zsh/zprofile\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eemulate sh -c \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;source /etc/profile.d/apps-bin-path.sh\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn case you don\u0026rsquo;t have \u003ccode\u003eapps-bin-path.sh\u003c/code\u003e in \u003ccode\u003e/etc/profile.d/\u003c/code\u003e, then here\u0026rsquo;s the file as of this date:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# shellcheck shell=sh\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Expand $PATH to include the directory where snappy applications go.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esnap_bin_path\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/snap/bin\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e -n \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003ePATH##*\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003esnap_bin_path\u003cspan style=\"color:#e6db74\"\u003e}}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -a -n \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003ePATH##*\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003esnap_bin_path\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e:*\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    export PATH\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$PATH:\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003esnap_bin_path\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Ensure base distro defaults xdg path are set if nothing filed up some\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# defaults yet.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e -z \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$XDG_DATA_DIRS\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    export XDG_DATA_DIRS\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/usr/local/share:/usr/share\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Desktop files (used by desktop environments within both X11 and Wayland) are\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# looked for in XDG_DATA_DIRS; make sure it includes the relevant directory for\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# snappy applications\u0026#39; desktop files.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esnap_xdg_path\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/var/lib/snapd/desktop\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e -n \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eXDG_DATA_DIRS##*\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003esnap_xdg_path\u003cspan style=\"color:#e6db74\"\u003e}}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -a -n \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eXDG_DATA_DIRS##*\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003esnap_xdg_path\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e:*\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    export XDG_DATA_DIRS\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003eXDG_DATA_DIRS\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e:\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003esnap_xdg_path\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSource: \u003ca href=\"https://askubuntu.com/users/263969/hackel\"\u003ehackel\u003c/a\u003e on \u003ca href=\"https://askubuntu.com/questions/910821/programs-installed-via-snap-not-showing-up-in-launcher\"\u003eAsk Ubuntu\u003c/a\u003e.\u003c/p\u003e\n",
    "date_published": "2020.01.25",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/offlinepip/",
    "url": "https://brandonrozek.com/blog/offlinepip/",
    "title": "Offline Pip Packages",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThere are a few reasons I can think of to have offline pip packages:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eA package isn\u0026rsquo;t able to compile on a friend\u0026rsquo;s computer since they don\u0026rsquo;t have the million linear algebra libraries that \u003ccode\u003enumpy\u003c/code\u003e /\u003ccode\u003escipy\u003c/code\u003e require.\u003c/li\u003e\n\u003cli\u003eYou want to archive everything to run a piece of software\u003c/li\u003e\n\u003cli\u003eYou want to control the packages available to a closed network\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eRegardless, to my surprise, setting up a repository of python wheels doesn\u0026rsquo;t take many steps.\u003c/p\u003e\n\u003ch2 id=\"setup\"\u003eSetup\u003c/h2\u003e\n\u003cp\u003eFirst I would recommend that you setup a virtual environment. Either through \u003ca href=\"/blog/pyenv/\"\u003epyenv\u003c/a\u003e or \u003ca href=\"/blog/virtualenv/\"\u003epython-virtualenv\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThen, install whatever packages you would like. Let us use tensorflow as an example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install tensorflow\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe\u0026rsquo;re going to need the packages \u003ccode\u003epip-chill\u003c/code\u003e and \u003ccode\u003epip-tools\u003c/code\u003e for the next couple steps\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install pip-chill pip-tools\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAfter you install all the packages you want to be available, freeze the requirements that aren\u0026rsquo;t dependencies to a text file\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip-chill --no-version \u0026gt; requirements.in\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe will then use \u003ccode\u003epip-compile\u003c/code\u003e in \u003ccode\u003epip-tools\u003c/code\u003e to resolve our dependencies and make our packages as fresh as possible.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip-compile requirements.in\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo sync the current virtual environment with the \u003ccode\u003erequirements.txt\u003c/code\u003e file that gets produced\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip-sync\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow we have a fully working and resolved environment.\u003c/p\u003e\n\u003cp\u003eFrom here, we need to install the wheel package to make the binary wheels.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install wheel\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen to create the wheels,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip wheel --wheel-dir\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ewheels -r requirements.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWith this you have a whole repository of wheels under the wheels folder!\u003c/p\u003e\n\u003ch2 id=\"client-side\"\u003eClient Side\u003c/h2\u003e\n\u003cp\u003eNow you can get \u003ca href=\"https://realpython.com/offline-python-deployments-with-docker/#deploy\"\u003eall fancy with your deployment\u003c/a\u003e, though I just assumed that the files were mounted in some shared folder.\u003c/p\u003e\n\u003cp\u003eThe client can install all the wheels\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install /path/to/wheels/*\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOr they can just install the packages they want\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install --no-index -f /path/to/wheels/wheels package_name\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you don\u0026rsquo;t want to add flags to every command, check out my post on using \u003ca href=\"/blog/pipconf/\"\u003econfiguration files with pip\u003c/a\u003e.\u003c/p\u003e\n",
    "date_published": "2020.01.20",
    "tags": ["Python","Archive"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/tempstaticip/",
    "url": "https://brandonrozek.com/blog/tempstaticip/",
    "title": "Temporary Static IP",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve learned that the fastest way to transfer files is via Ethernet. Now the easiest way to transfer via Ethernet is for both computers to be on the same local area network. However, if needed, an Ethernet cable can used between two computers.\u003c/p\u003e\n\u003cp\u003eIn order for both the computers to recognize each other, they need to be on the same subnet.\u003c/p\u003e\n\u003cp\u003eFirst on both computers, find out which network device is your Ethernet.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eip addr show\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen on both computers assign an unique static ip address,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo ip addr add 10.1.1.2/24 dev en0\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn this example,\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003e10.1.1\u003c/code\u003e is the subnet that the computer is assigned to.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e2\u003c/code\u003e will denote the unique ip address\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e/24\u003c/code\u003e defines the first three digits as the subnet\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003een0\u003c/code\u003e is the example network device that\u0026rsquo;s the Ethernet.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eOnce that\u0026rsquo;s in place you should be able to \u003ccode\u003eping\u003c/code\u003e and \u003ccode\u003ersync\u003c/code\u003e between the two computers!\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eIf you run into issues, it might be a firewall problem\u0026hellip;\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eTo remove the static ip assignment, replace \u003ccode\u003eadd\u003c/code\u003e with \u003ccode\u003edel\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo ip addr del 10.1.1.2/24 dev en0\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.01.20",
    "tags": ["Linux","Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/mirrordownload/",
    "url": "https://brandonrozek.com/blog/mirrordownload/",
    "title": "Mirror Download with wget",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis post will describe downloading a \u003ccode\u003ecentos\u003c/code\u003e repo using \u003ccode\u003ewget\u003c/code\u003e. Though the ideas in this blog post can apply to any mirror with packages exposed via http.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewget \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --accept rpm,bz2,gz,xml,asc \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --recursive \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --no-parent \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --no-host-directories \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --cut-dirs\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  http://mirror.centos.org/centos/8/BaseOS/x86_64/os/ \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHere are what the options mean\u0026hellip;\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eOption\u003c/th\u003e\n          \u003cth\u003eMeaning\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--accept\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eComma separated by which extensions to allow downloading\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--recursive\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eFollow links\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--no-parent\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eOnly follow links that are sub-directories of the current one\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--no-host-directories\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eExclude creating a folder indicating the hostname\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003e--cut-dirs=N\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eDon\u0026rsquo;t make folders for a depth of \u003ccode\u003eN\u003c/code\u003e subdirectories. Notice in the example \u003ccode\u003ecentos\u003c/code\u003e, \u003ccode\u003e8\u003c/code\u003e, \u003ccode\u003eBaseOS\u003c/code\u003e, \u003ccode\u003ex86_64\u003c/code\u003e, \u003ccode\u003eos\u003c/code\u003e is a list of 5 subdirectories so \u003ccode\u003eN\u003c/code\u003e=5\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n",
    "date_published": "2020.01.20",
    "tags": ["Linux","Archive"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/ddforiso/",
    "url": "https://brandonrozek.com/blog/ddforiso/",
    "title": "Burning ISOs with dd/pv",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhile there are nice graphical tools like \u003ca href=\"https://www.balena.io/etcher/\"\u003eEtcher\u003c/a\u003e, what is almost always a constant is the tool \u003ccode\u003edd\u003c/code\u003e. Therefore, for future reference I\u0026rsquo;ll just paste the \u003ccode\u003edd\u003c/code\u003e command I use to make ISO images.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dd bs\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e4M \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eimage.iso of\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/dev/sdX status\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eprogress oflag\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003esync\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAlternatively you can use \u003ccode\u003epv\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epv image.iso \u0026gt; /dev/sdX\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.01.20",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pyenv/",
    "url": "https://brandonrozek.com/blog/pyenv/",
    "title": "Managing Python Versions with Pyenv",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI wrote previously about \u003ca href=\"/blog/virtualenv/\"\u003emanaging python virtual environments\u003c/a\u003e. Since then, I\u0026rsquo;ve discovered a software called \u003ca href=\"https://github.com/pyenv/pyenv\"\u003ePyenv\u003c/a\u003e which allows you to not only manage virtual environments but python versions. As someone who likes to develop python programs in his free time, I found this incredibly useful in keeping all my virtual environments in one place and easily upgrading to a more recent version of python.\u003c/p\u003e\n\u003cp\u003eTo install, follow the steps outlined in the \u003ca href=\"https://github.com/pyenv/pyenv-installer\"\u003epyenv-istaller\u003c/a\u003e repository. As of now, it\u0026rsquo;s a bash script.\u003c/p\u003e\n\u003ch2 id=\"python-versions\"\u003ePython Versions\u003c/h2\u003e\n\u003cp\u003eOnce it\u0026rsquo;s installed, we need to download a version of python. To see what\u0026rsquo;s available,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv install --list\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will give us a very large list of all the different python versions we can install. Let\u0026rsquo;s say we want to install vanilla Python version 3.8.1. Then all we do is specify that after the install keyword,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv install 3.8.1\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow pyenv has a great concept where you can set certain folders to automatically load a specific python version or virtual environment. To set the current directory to use python 3.8.1 by default, run the following command:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv local 3.8.1\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will then create a file called \u003ccode\u003e.python-version\u003c/code\u003e which contains the text \u003ccode\u003e3.8.1\u003c/code\u003e. To set this globally, just replace \u003ccode\u003elocal\u003c/code\u003e with \u003ccode\u003eglobal\u003c/code\u003e,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv global 3.8.1\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFinally, if you only want to specify a python version for the current shell.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv shell 3.8.1\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"python-virtual-environments\"\u003ePython Virtual Environments\u003c/h2\u003e\n\u003cp\u003eTo create a virtual environment, run the following\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv virtualenv name\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will create a virtual environment called \u003ccode\u003ename\u003c/code\u003e that is bound to the version of Python you have enabled at the current moment.\u003c/p\u003e\n\u003cp\u003eOnce it\u0026rsquo;s created you can activate it with the following,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv activate name\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can also use the \u003ccode\u003elocal\u003c/code\u003e and \u003ccode\u003eglobal\u003c/code\u003e pyenv commands to set the current directory or default to be the virtual environment. To do that, all we need to do is replace the python version from above with the environment name\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv local name\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWith this, you now have a python virtual environment to play with!\u003c/p\u003e\n\u003ch2 id=\"other-useful-commands\"\u003eOther useful commands\u003c/h2\u003e\n\u003cp\u003eWhich version or virtual environment am I using?\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv version\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhich versions or virtual environments are available to me?\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv versions\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhich virtual environment or version contains a command I\u0026rsquo;m looking for? (e.g spyder)\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epyenv whence executable\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.01.20",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/limitbandwidth/",
    "url": "https://brandonrozek.com/blog/limitbandwidth/",
    "title": "Limit Bandwidth through Terminal",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eHave you ever wondered how an application or a system would operate under low bandwidth environments? Luckily \u003ccode\u003ewondershaper\u003c/code\u003e is a tool to help with just that!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install wondershaper\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo get started, first find out the network \u003ccode\u003einterface\u003c/code\u003e that you want to throttle.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eip addr show\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo show the state of the interface,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo wondershaper \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003einterface\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo set the bandwidth,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo wondershaper \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003einterface\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003edownlink\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003euplink\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003ewhere downlink and uplink are defined in kilobits per second.\u003c/p\u003e\n\u003cp\u003eTo clear the rules,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo wondershaper clear \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003einterface\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.01.15",
    "tags": ["Linux","Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/xpra/",
    "url": "https://brandonrozek.com/blog/xpra/",
    "title": "Persistent remote X sessions with xpra",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"http://xpra.org/\"\u003e\u003ccode\u003expra\u003c/code\u003e\u003c/a\u003e allows one to run persistent X applications on a remote host and display it on a local machine. It\u0026rsquo;s a combination of \u003ca href=\"https://wiki.archlinux.org/index.php/OpenSSH#X11_forwarding\"\u003eSSH X11 Forwarding\u003c/a\u003e and \u003ca href=\"https://www.gnu.org/software/screen/\"\u003eScreen\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eTo get started you need to install the \u003ccode\u003expra\u003c/code\u003e package on both the server and client. On Ubuntu 18.04, this package isn\u0026rsquo;t configured properly so one should use this PPA instead.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo add-apt-repository ppa:mikhailnov/xpra\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo install,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install xpra\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow you can from the client open up an application with one command\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003expra start ssh:user@host --exit-with-children --start-child\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;executable\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you want it to behave more like screen. Then on the server.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003expra start :100\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhere you can replace \u003ccode\u003e:100\u003c/code\u003e with another high display number.\u003c/p\u003e\n\u003cp\u003eThen you can run the executable,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDISPLAY\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e:100 executable\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFrom the client,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003expra attach ssh:user@host:100\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003expra\u003c/code\u003e has heuristics that determines the encoding of the images passed. You can however override it using the \u003ccode\u003e--encoding\u003c/code\u003es flag to better tailor to your needs.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003ergb\u003c/code\u003e: Raw pixel format that is lossless and uses compression. Best in high bandwidth environments.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epng\u003c/code\u003e compressed, lossless, but CPU intensive. May result in skipped frames\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eh264\u003c/code\u003e, \u003ccode\u003evp8\u003c/code\u003e, \u003ccode\u003evp9\u003c/code\u003e are lossy formats that have tunable quality and speed parameters\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eMore resources:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://wiki.archlinux.org/index.php/Xpra\"\u003eArch Wiki\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://help.ubuntu.com/community/Xpra\"\u003eUbuntu Wiki\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n",
    "date_published": "2020.01.15",
    "tags": ["X11"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/mergerfs/",
    "url": "https://brandonrozek.com/blog/mergerfs/",
    "title": "Balance files across multiple disks using MergerFS",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://github.com/trapexit/mergerfs\"\u003eMergerFS\u003c/a\u003e is a great filesystem for an expandable storage system in a homelab. Mostly since it allows you to add disks one at a time without having to, for example, resilver a ZFS pool. MergerFS won\u0026rsquo;t be as efficient as a filesystem that stripes your data across disks, but in the case of a disk failure the disks unaffected will still have the files assigned to them.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://blog.linuxserver.io/2017/06/24/the-perfect-media-server-2017/\"\u003ePlenty\u003c/a\u003e of other \u003ca href=\"https://web.archive.org/web/20200130103849/https://www.teknophiles.com/2018/02/19/disk-pooling-in-linux-with-mergerfs/\"\u003epeople\u003c/a\u003e described MergerFS, so I\u0026rsquo;ll keep this post simple.\u003c/p\u003e\n\u003cp\u003eFirst install MergerFS,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install mergerfs\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe way I have my drives in my homelab setup is to have \u003ccode\u003e/mnt/data/N\u003c/code\u003e where \u003ccode\u003eN\u003c/code\u003e is the number of the drive.\u003c/p\u003e\n\u003cp\u003eExamples: \u003ccode\u003e/mnt/data/1\u003c/code\u003e, \u003ccode\u003e/mnt/data/2\u003c/code\u003e, \u003ccode\u003e/mnt/data/3\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eThis is mainly so that I can use wildcards to capture all the drives at once.\u003c/p\u003e\n\u003cp\u003eTemporary mounting solution:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mergerfs -o defaults,allow_other,use_ino,fsname\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003edata /mnt/data/\u003cspan style=\"color:#ae81ff\"\u003e\\*\u003c/span\u003e $HOME/data\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003ePermanent solution (Edit \u003ccode\u003e/etc/fstab\u003c/code\u003e)\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e/mnt/data/* /home/user/data fuse.mergerfs defaults,allow_other,use_ino,fsname\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003edata \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eQuick summary of options passed\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eOption\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003edefaults\u003c/td\u003e\n          \u003ctd\u003eShortcut for atomic_o_trunc, auto_cache, big_writes, default_permissions, splice_move, splice_read, splice_write\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eallow_other\u003c/td\u003e\n          \u003ctd\u003eAllows users beside the mergerfs owner to view the filesystem.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003euse_ino\u003c/td\u003e\n          \u003ctd\u003eMergerFS supplies inodes instead of libfuse\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003efsname\u003c/td\u003e\n          \u003ctd\u003eName of the mount as shown in \u003ccode\u003edf\u003c/code\u003e and other tools\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n",
    "date_published": "2020.01.14",
    "tags": ["Linux","Storage"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pythonpathhacks/",
    "url": "https://brandonrozek.com/blog/pythonpathhacks/",
    "title": "Python Path Hacks",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThere are two quick ways to hack together custom imports in Python. One is by using the \u003ccode\u003ePYTHONPATH\u003c/code\u003e environmental variable, and the other way is by using the \u003ccode\u003esys\u003c/code\u003e module in Python.\u003c/p\u003e\n\u003ch2 id=\"method-1-pythonpath\"\u003eMethod 1: \u003ccode\u003ePYTHONPATH\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eBefore you call \u003ccode\u003epython\u003c/code\u003e set the environmental variable.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePYTHONPATH\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$PYTHONPATH:/other/path\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen you can call \u003ccode\u003epython\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"method-2-sys\"\u003eMethod 2: \u003ccode\u003esys\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eIn the beginning of your Python script add the following code.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e sys\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esys\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/other/path\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"remarks\"\u003eRemarks\u003c/h2\u003e\n\u003cp\u003eOf course this isn\u0026rsquo;t the best way to add custom libraries to your Python scripts. Ideally, we would use \u003ccode\u003epip\u003c/code\u003e to manage our dependencies. Perhaps a future blog post will cover this!\u003c/p\u003e\n",
    "date_published": "2020.01.13",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/cryptogames/",
    "url": "https://brandonrozek.com/blog/cryptogames/",
    "title": "Cryptographic Games",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen analyzing cryptographic algorithms, we characterize the strength of the crypto-system by analyzing what happens in various crypto games. Below are a couple examples of crypto games used in literature.\u003c/p\u003e\n\u003cp\u003eActors typically involved in crypto games:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eActor\u003c/th\u003e\n          \u003cth\u003eRole\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eOracle\u003c/td\u003e\n          \u003ctd\u003eEncrypts a given message.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eChallenger\u003c/td\u003e\n          \u003ctd\u003eSets up the game between the oracle and adversary.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eAdversary\u003c/td\u003e\n          \u003ctd\u003eSends messages to the oracle and makes a guess at the end.\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch2 id=\"left-right-game\"\u003eLeft-Right Game\u003c/h2\u003e\n\u003cp\u003eHere we will have two oracle\u0026rsquo;s \u003ccode\u003eleft\u003c/code\u003e and \u003ccode\u003eright\u003c/code\u003e both with a random key $k$.\u003c/p\u003e\n\u003cp\u003eThe challenger will create a random bit $b$ which is either $0$ or $1$.\u003c/p\u003e\n\u003cp\u003eIf the bit is $0$, then the adversary will send messages to \u003ccode\u003eleft\u003c/code\u003e. Otherwise, the adversary will send messages to \u003ccode\u003eright\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eAfter a certain number of interactions the adversary needs to guess which oracle it is talking to.\u003c/p\u003e\n\u003cp\u003eWe define the adversary\u0026rsquo;s advantage by the distance the probability is away from $\\frac{1}{2}$.\n$$\nadvantage = |p - \\frac{1}{2}|\n$$\nWhy the absolute value? Well if we only guess correctly $10%$ of the time, then we just need to invert our guess to be correct $90%$ of the time.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eGenerate key for both oracles\nGenerate random bit b\nwhile game not done:\n  advesary generates message m\n  if b = 0:\n    send m to left oracle\n    receive c from left oracle\n  else:\n    send m to right oracle\n    receive c from right oracle\nAdversary guesses whether b = 0 or 1\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"real-random-game\"\u003eReal-Random Game\u003c/h2\u003e\n\u003cp\u003eThis game is very similar to the last one except one of the oracle\u0026rsquo;s only produces random bitstrings.\u003c/p\u003e\n\u003cp\u003eAn oracle is initialized with a random key $k$ and the challenger creates a random bit $b$.\u003c/p\u003e\n\u003cp\u003eIf $b = 0$, then the adversary will send messages to the oracle with the proper encryption function and will receive ciphertexts. Otherwise, the adversary will send messages to the random bitstring generator.\u003c/p\u003e\n\u003cp\u003eAt the end of the game, the adversary needs to guess whether its talking to a proper oracle or a random bitstring generator.\u003c/p\u003e\n\u003cp\u003eThe same metric \u003ccode\u003eadversary's advantage\u003c/code\u003e is used to characterize the strength of a crypto-system as the last one.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eGenerate key for both oracles\nGenerate random bit b\nwhile game not done:\n  advesary generates message m\n  if b = 0:\n    send m to proper oracle\n    receive c from proper oracle\n  else:\n    send m to random oracle\n    receive random bitstring c from random oracle\nAdversary guesses whether b = 0 or 1\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"random-function---random-permutation-trick\"\u003eRandom Function - Random Permutation Trick\u003c/h2\u003e\n\u003cp\u003eOne trick that we can use in proofs is to say that two procedures are the same up to a certain point when it diverges.\u003c/p\u003e\n\u003cp\u003eLet us define $f$ to be some function that given a bitstring $X$ produces a random bitstring $Y$.\u003c/p\u003e\n\u003cp\u003eNow, $image(f)$ is initially undefined since we don\u0026rsquo;t know which bitstrings will be randomly generated.\u003c/p\u003e\n\u003cp\u003eHowever, as we calculate $Y$ we will store it to build up $image(f)$.\u003c/p\u003e\n\u003cp\u003eAt some point, we\u0026rsquo;ll generate $Y$ that already exists in $image(f)$. In that moment we will split based off of whether the procedure is a random function or random permutation.\u003c/p\u003e\n\u003cp\u003eIf random function, then just return the same $Y$ and end the procedure. Otherwise, return a random bitstring outside of the $range(f)$ and end the procedure.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eInitialize function f with range(f) empty\nGenerate different random bitstring X\nWhile f(X) not in range(f):\n  Add f(X) to range(f)\n  Generate different random bitstring X\nIf random function:\n  return f(X)\nElse:\n  return random bitstring Y not in range(f)\n\u003c/code\u003e\u003c/pre\u003e",
    "date_published": "2020.01.13",
    "tags": ["Security"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/virtuallivecd/",
    "url": "https://brandonrozek.com/blog/virtuallivecd/",
    "title": "Playing with Live CDs",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI was curious on how Lubuntu 19.10 looked but I didn\u0026rsquo;t feel like rebooting my computer and loading into a new ISO. Luckily there is a nice easy way to play around with live CDs.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s the command\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003evirt-install --name\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eLubuntu \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --nodisks  --livecd \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --graphics spice \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --vcpu\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --ram\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e4096\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --os-type\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003elinux \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --sound \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --accelerate \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e  --cdrom\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$HOME/Downloads/lubuntu-19.10-desktop-amd64.iso\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe arguments mean the following\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eArgument\u003c/th\u003e\n          \u003cth\u003eMeaning\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003enodisks\u003c/td\u003e\n          \u003ctd\u003eNo storage disks are created\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003elivecd\u003c/td\u003e\n          \u003ctd\u003eSet the boot to the cdrom after installation\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003egraphics\u003c/td\u003e\n          \u003ctd\u003eSets the graphics mode\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003evcpu\u003c/td\u003e\n          \u003ctd\u003eNumber of virtual CPUs\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eRAM\u003c/td\u003e\n          \u003ctd\u003eSize of RAM\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eos-type\u003c/td\u003e\n          \u003ctd\u003eLinux/Windows/etc.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003esound\u003c/td\u003e\n          \u003ctd\u003eAttach a virtual audio device\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eaccelerate\u003c/td\u003e\n          \u003ctd\u003eMake use of the KVM or KQEMU kernel acceleration capabilities if available.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ecdrom\u003c/td\u003e\n          \u003ctd\u003eLocation of ISO\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eOnce you run this command once, the image is \u0026ldquo;installed\u0026rdquo;. This means that we can easily access it in the future with the following commands\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003evirsh start Lubuntu\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003evirt-viewer Lubuntu\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/1578887790276.png\" alt=\"1578887790276\"\u003e\u003c/p\u003e\n",
    "date_published": "2020.01.12",
    "tags": ["Virtualization"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/quickstoragesetup/",
    "url": "https://brandonrozek.com/blog/quickstoragesetup/",
    "title": "Quickly Setting up a Storage Device",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis post exists mostly to aid myself for when I buy new drives for my home server. It\u0026rsquo;s a quick and easy way to create an ext4 filesystem over the entire drive.\u003c/p\u003e\n\u003cp\u003eTo go through this post, you\u0026rsquo;ll need to know the name of your drive.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo fdisk -l\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eor\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elsblk\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe drive is most likely one of the larger devices with no partitions set. It\u0026rsquo;ll likely be of the format \u003ccode\u003e/dev/sdX\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eTo begin, we\u0026rsquo;ll have to set the label. Here we\u0026rsquo;ll use \u003ccode\u003egpt\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo parted /dev/sdX mklabel gpt\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen we can create a primary partition formatted with ext4 covering the entire device.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo parted -a opt /dev/sdX mkpart primary ext4 0% 100%\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow we can let \u003ccode\u003eext4\u003c/code\u003e format the drive,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mkfs.ext4 /dev/sdX1\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI like to set up my mount points to be \u003ccode\u003e/mnt/data/N\u003c/code\u003e where N is the number of the drive I\u0026rsquo;m working with.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mkdir /mnt/data/N\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo temporarily mount it, just to make sure it works you can run\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mount /dev/sdX1 /mnt/data/N\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can unmount it with \u003ccode\u003eumount\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo umount /dev/sdX1\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhen you\u0026rsquo;re ready to make it permanent, we\u0026rsquo;ll have to edit the \u003ccode\u003e/etc/fstab\u003c/code\u003e file. We should note the drive by its UUID so that it\u0026rsquo;s not dependent on the slot the hard drive sits in. You can find it by running this command\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elsblk -o UUID /dev/sdX1\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow you can append your \u003ccode\u003e/etc/fstab\u003c/code\u003e with the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eUUID\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003euuid-here /mnt/data/N ext4 defaults \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2020.01.12",
    "tags": ["Linux","Storage"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/scrcpy/",
    "url": "https://brandonrozek.com/blog/scrcpy/",
    "title": "Control an Android Device Remotely using Scrcpy",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWith \u003ca href=\"https://github.com/Genymobile/scrcpy\"\u003eScrcpy\u003c/a\u003e you can control an Android device remotely!\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/1578623897330.png\" alt=\"1578623897330\"\u003e\u003c/p\u003e\n\u003cp\u003eThe \u003ccode\u003eREADME\u003c/code\u003e on the Github page has all the information you need though it boils down to a few simple steps.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eInstall \u003ccode\u003escrcpy\u003c/code\u003e.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo snap install scrcpy\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col start=\"2\"\u003e\n\u003cli\u003e\u003ca href=\"https://developer.android.com/studio/command-line/adb.html#Enabling\"\u003eEnable adb debugging\u003c/a\u003e on your device\u003c/li\u003e\n\u003cli\u003e(Optional for remote capability) Enable adb over TCP/IP on your device\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eadb tcpip \u003cspan style=\"color:#ae81ff\"\u003e5555\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col start=\"4\"\u003e\n\u003cli\u003e(Optional Continued) Connect to the device\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eadb connect DEVICE_IP:5555\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col start=\"5\"\u003e\n\u003cli\u003eRun \u003ccode\u003escrcpy\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n",
    "date_published": "2020.01.09",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/togglingxinput/",
    "url": "https://brandonrozek.com/blog/togglingxinput/",
    "title": "Toggling X Input",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOn X, we can easily enable or disable input devices using the \u003ccode\u003exinput\u003c/code\u003e command. This is a great use case when you\u0026rsquo;re tired of accidentally hitting the red Thinkpad nub or having your palm be recognized when drawing with a pen.\u003c/p\u003e\n\u003cp\u003eRunning the \u003ccode\u003exinput\u003c/code\u003e command performs the action temporarily. Your default settings will be restored upon a reboot.\u003c/p\u003e\n\u003cp\u003eTo list \u003ccode\u003exinput\u003c/code\u003e devices run:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003exinput\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo disable a device:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003exinput disable \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eid\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo enable a device:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003exinput enable \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eid\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe ids are listed when you list the devices.\u003c/p\u003e\n\u003cp\u003eTo query whether the device is enabled or disabled:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003exinput --list-props \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eid\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e | grep \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Device Enabled\u0026#34;\u003c/span\u003e | awk \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;{ print $NF }\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will return $1$ or $0$ depending on if its enabled or not respectively.\u003c/p\u003e\n",
    "date_published": "2020.01.07",
    "tags": ["Linux","X11"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/virtualdisks/",
    "url": "https://brandonrozek.com/blog/virtualdisks/",
    "title": "Playing with Filesystems using Virtual Disks",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eHave you wanted to \u003ca href=\"https://wiki.archlinux.org/index.php/ZFS/Virtual_disks\"\u003eplay with ZFS\u003c/a\u003e or any other filesystem without creating a dedicated partition or device? We can do this through virtual disks!\u003c/p\u003e\n\u003cp\u003eFirst, we need to create a \u003ca href=\"https://en.wikipedia.org/wiki/Sparse_file\"\u003esparse file\u003c/a\u003e called \u003ccode\u003escratch.img\u003c/code\u003e with some max capacity. Let\u0026rsquo;s say 2G.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etruncate -s 2G $HOME/scratch.img\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow the file is only sparse, if your filesystem supports it. To see, run \u003ccode\u003edu -sh $HOME/scratch.img\u003c/code\u003e. If it says that the size is \u003ccode\u003e0\u003c/code\u003e then your filesystem supports sparse files. Otherwise it does not.\u003c/p\u003e\n\u003cp\u003eThen, we can format the file with any filesystem we will like. One popular tool is \u003ccode\u003emkfs\u003c/code\u003e which depending on your operating system can support \u003ca href=\"https://en.wikipedia.org/wiki/Be_File_System\"\u003e\u003ccode\u003ebfs\u003c/code\u003e\u003c/a\u003e, \u003ca href=\"https://en.wikipedia.org/wiki/Cramfs\"\u003e\u003ccode\u003ecramfs\u003c/code\u003e\u003c/a\u003e, \u003ca href=\"https://en.wikipedia.org/wiki/Ext2\"\u003e\u003ccode\u003eext2\u003c/code\u003e\u003c/a\u003e, \u003ca href=\"https://en.wikipedia.org/wiki/Ext3\"\u003e\u003ccode\u003eext3\u003c/code\u003e\u003c/a\u003e, \u003ca href=\"https://en.wikipedia.org/wiki/Ext4\"\u003e\u003ccode\u003eext4\u003c/code\u003e\u003c/a\u003e, \u003ca href=\"https://en.wikipedia.org/wiki/File_Allocation_Table\"\u003e\u003ccode\u003efat\u003c/code\u003e\u003c/a\u003e, \u003ca href=\"https://en.wikipedia.org/wiki/MINIX_file_system\"\u003e\u003ccode\u003eminix\u003c/code\u003e\u003c/a\u003e, \u003ccode\u003emsdos\u003c/code\u003e, \u003ca href=\"https://en.wikipedia.org/wiki/NTFS\"\u003e\u003ccode\u003entfs\u003c/code\u003e\u003c/a\u003e, \u003ca href=\"https://en.wikipedia.org/wiki/File_Allocation_Table#VFAT\"\u003e\u003ccode\u003evfat\u003c/code\u003e\u003c/a\u003e, \u003ca href=\"https://en.wikipedia.org/wiki/ReiserFS\"\u003e\u003ccode\u003ereiserfs\u003c/code\u003e\u003c/a\u003e, etc.\u003c/p\u003e\n\u003cp\u003eTo format with \u003ccode\u003eext4\u003c/code\u003e,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkfs -t ext4 $HOME/scratch.img\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen we can create the mount-point \u003ccode\u003e/mnt/scratch\u003c/code\u003e and mount \u003ccode\u003escratch.img\u003c/code\u003e to it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mkdir /mnt/scratch\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mount -t auto -o loop $HOME/scratch.img /mnt/scratch\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWith this, we now have a mounted \u003ccode\u003eext4\u003c/code\u003e filesystem on \u003ccode\u003e/mnt/scratch\u003c/code\u003e. \u003ccode\u003ecd\u003c/code\u003e into it and have a blast!\u003c/p\u003e\n\u003ch2 id=\"resizing-the-virtual-disk\"\u003eResizing the Virtual Disk\u003c/h2\u003e\n\u003cp\u003eTo resize the virtual disk, we will first need to unmount the virtual disk so we don\u0026rsquo;t create inconsistencies.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo umount /mnt/scratch\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow we can extend or shrink the drive with \u003ccode\u003etruncate\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eExtend by 1G: \u003ccode\u003etruncate -s +1G $HOME/scratch.img\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eShrink by 1G: \u003ccode\u003etruncate -s -1G $HOME/scratch.img\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eCheck the filesystem to make sure that no inconsistencies occurred. With \u003ccode\u003eext(2/3/4)\u003c/code\u003e we can do this with the \u003ccode\u003ee2fsck\u003c/code\u003e command.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ee2fsck $HOME/scratch.img\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen we need to tell the filesystem to resize itself. For \u003ccode\u003eext(2/3/4)\u003c/code\u003e you can do this with the \u003ccode\u003eresize2fs\u003c/code\u003e command.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eresize2fs $HOME/scratch.img\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow the virtual disk is successfully resized! We can mount it back with the previous mount command.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mount -t auto -o loop $HOME/scratch.img /mnt/scratch\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"removing-the-virtual-disk\"\u003eRemoving the Virtual Disk\u003c/h2\u003e\n\u003cp\u003eTo remove the virtual disk, we first need to unmount the virtual drive\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo umount /mnt/scratch\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen we can remove the mount-point and file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo rm -r /mnt/scratch\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erm $HOME/scratch.img\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eWith virtual disks we can experiment with different types of filesystems and perhaps try out snapshoting in supported filesystems. If we create virtual disks on \u003ca href=\"/blog/lxdtmpfs/\"\u003e\u003ccode\u003etmpfs\u003c/code\u003e \u003c/a\u003e, then we can have a super fast file system as well!\u003c/p\u003e\n",
    "date_published": "2020.01.06",
    "tags": ["Linux","Storage"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/tempresolve/",
    "url": "https://brandonrozek.com/blog/tempresolve/",
    "title": "Temporarily Resolving Hostnames",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLet\u0026rsquo;s say that we\u0026rsquo;re testing a webserver where the pages served depended on a domain that you don\u0026rsquo;t own. The most common way I know to test this is to modify your \u003ccode\u003e/etc/hosts\u003c/code\u003e file to contain the hostname and ip address you want to map it to.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e192.168.1.2   custom.domain\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eI\u0026rsquo;ve recently discovered that the command line utility \u003ccode\u003ecurl\u003c/code\u003e has a quick and easy option to forge the hostname of a request.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl --resolve domain:port:ipaddr url\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThere are also browser extensions that you can use such as \u003ca href=\"https://github.com/Aioros/livehosts\"\u003eLiveHosts\u003c/a\u003e to get around this as well. This post isn\u0026rsquo;t entirely useful when talking about permanent services.\u003c/p\u003e\n\u003cp\u003eIf this is going to be a publicly facing service, then you should just set the records of your domain name to point to the server.\u003c/p\u003e\n\u003cp\u003eIf it\u0026rsquo;s a non-public routable service, then perhaps try looking into setting up your own private \u003ca href=\"/blog/coredns/\"\u003edns server\u003c/a\u003e.\u003c/p\u003e\n",
    "date_published": "2020.01.04",
    "tags": ["Linux","Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/lxdtmpfs/",
    "url": "https://brandonrozek.com/blog/lxdtmpfs/",
    "title": "LXD on tmpfs",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eContainer images are designed to be as small as possible. Wouldn\u0026rsquo;t it be cool if we can hold entire containers in RAM? This post outlines how to accomplish this using LXD. It turns out that it is a lot easier to setup custom storage pools on LXD than with Docker.\u003c/p\u003e\n\u003ch2 id=\"setting-up-tmpfs\"\u003eSetting up tmpfs\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003etmpfs\u003c/code\u003e is a temporary filesystem that resides in memory. To get started, first create a directory that you want to mount with \u003ccode\u003etmpfs\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir /tmp/ramdisk\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo only do this temporarily, then you can run the following command\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mount -t tmpfs -o size\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e4G myramdisk /tmp/ramdisk\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can replace \u003ccode\u003e4G\u003c/code\u003e with any size less than your current RAM size.\u003c/p\u003e\n\u003cp\u003eTo set it up permanently, you will have to edit \u003ccode\u003e/etc/fstab\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003emyramdisk  /tmp/ramdisk  tmpfs  defaults,size=3G,x-gvfs-show  0  0\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"setting-up-lxd\"\u003eSetting up LXD\u003c/h2\u003e\n\u003cp\u003eLXD is a lightweight container hypervisor in Linux. To install and setup, please follow the beginning sections of \u003ca href=\"https://linuxcontainers.org/lxd/getting-started-cli/\"\u003ehttps://linuxcontainers.org/lxd/getting-started-cli/\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eOnce you\u0026rsquo;ve setup the initial configuration with \u003ccode\u003elxd init\u003c/code\u003e, we can create a new storage pool using our newly created \u003ccode\u003etmpfs\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elxc storage create tmplabel dir source\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/tmp/ramdisk \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe can then create a container that will use this storage pool\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elxc launch ubuntu:18.04 mycontainer -s tmplabel\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn the last command, the \u003ccode\u003e-s\u003c/code\u003e flag indicates which storage pool we want to use.\u003c/p\u003e\n\u003cp\u003eWith this, our entire container filesystem lives in RAM!\u003c/p\u003e\n\u003ch2 id=\"optional-setting-up-mounts\"\u003e(Optional) Setting up mounts\u003c/h2\u003e\n\u003cp\u003eOne downside to putting the entire filesystem in RAM is that it isn\u0026rsquo;t persistent across reboots. You can think of the container then as ephemeral and setup mountpoints to the host in order to save important configuration information.\u003c/p\u003e\n\u003cp\u003eIf the mount point is configured to be world writable or you only need it to be readonly, then this is very simple to setup!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elxc config device add mycontainer dirlabel disk source\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/path/on/host path\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/path/on/container\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis is because files in the container are marked as \u003ccode\u003enobody:nogroup\u003c/code\u003e. If you want to be able to write to the mounted directory that\u0026rsquo;s not setup to be world-writable then there\u0026rsquo;s extra steps we need to take.\u003c/p\u003e\n\u003cp\u003eMost of the following information is taken from: \u003ca href=\"https://tribaal.io/nicer-mounting-home-in-lxd.html\"\u003ehttps://tribaal.io/nicer-mounting-home-in-lxd.html\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s say that you want LXD to be able to write to a folder that you own. First we need to allow LXD to remap your user ID.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;root:\u003c/span\u003e$UID\u003cspan style=\"color:#e6db74\"\u003e:1\u0026#34;\u003c/span\u003e | sudo tee -a /etc/subuid /etc/subgid\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe only need to do this once per host system.\u003c/p\u003e\n\u003cp\u003eThe Ubuntu container has a user called \u003ccode\u003eubuntu\u003c/code\u003e with UID 1000. We can remap that userid, otherwise you would have to create another user on the container to remap.\u003c/p\u003e\n\u003cp\u003eIf you create another user, make sure you get its id for the next command:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# In the container\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eid -u username\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor the rest of this tutorial, we will assume that you have a user named ubuntu with UID 1000.\u003c/p\u003e\n\u003cp\u003eOnce LXD is able to remap your user id, you can tell it to do so for the container of interest.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elxc config set mycontainer raw.idmap \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;both \u003c/span\u003e$UID\u003cspan style=\"color:#e6db74\"\u003e 1000\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow to setup the mount\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elxc config device add myubuntu homedir disk source\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e$HOME path\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/home/ubuntu\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRestart the container and now the \u003ccode\u003eubuntu\u003c/code\u003e user will be able to write to the mount!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elxc restart mycontainer\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.12.31",
    "tags": ["Linux","Storage","Containers"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/proofdef/",
    "url": "https://brandonrozek.com/blog/proofdef/",
    "title": "Theorem Proving Definitions",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen I look into a new field, sometimes I get confused by the whole new set of vocab terms I need to encounter. This post will serve to keep me straight with the terms involved in theorem proving.\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eWord\u003c/th\u003e\n          \u003cth\u003eDefinition\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eModus Ponens\u003c/td\u003e\n          \u003ctd\u003eIf $P$ implies $Q$ and $P$ is asserted to be true, then $Q$ must be true.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eComplete\u003c/td\u003e\n          \u003ctd\u003eIf every formula having the property can be derived using the system.  (i.e The system does not miss a result)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eNegation-Complete\u003c/td\u003e\n          \u003ctd\u003eEither $\\phi$ or $\\neg \\phi$ can be proved in the system.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eConsistent\u003c/td\u003e\n          \u003ctd\u003eFor any provable formula $\\phi$, the negation ($\\neg \\phi$) cannot be provable. (Cannot derive a contradiction)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eDecidable\u003c/td\u003e\n          \u003ctd\u003eAn effective method exists for deriving the correct answer in a finite time.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eSound\u003c/td\u003e\n          \u003ctd\u003eEvery formula that can be proved in the system is logically valid with respect to the semantics of the system. (i.e The system does not produce a wrong result)\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eHopefully, I\u0026rsquo;ll come back and add more terms as I get confused.\u003c/p\u003e\n",
    "date_published": "2019.12.29",
    "tags": ["Formal Methods"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/systemdstartup/",
    "url": "https://brandonrozek.com/blog/systemdstartup/",
    "title": "Analyzing Startup Times with Systemd",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eStartup times feeling slow? Check to see if there are any uneeded services slowing you down!\u003c/p\u003e\n\u003cp\u003eTo see how long it takes to bootup\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemd-analyze\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo see the length of time each service took to initialize\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esystemd-analyze blame\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen you can \u003ccode\u003edisable\u003c/code\u003e any services that you don\u0026rsquo;t need.\u003c/p\u003e\n",
    "date_published": "2019.12.26",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/docker-image-bash/",
    "url": "https://brandonrozek.com/blog/docker-image-bash/",
    "title": "Advanced Docker Image Construction with Bash",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOn current versions of Docker, you can\u0026rsquo;t mount volumes during image construction. This poses an issue for me as I don\u0026rsquo;t want to replicate gigabytes of data already existing on my disk when it won\u0026rsquo;t appear on the final build. Therefore, instead of building an image with a traditional Dockerfile, we\u0026rsquo;re going to use a bash script on a running base image container and export the filesystem to create the image from.\u003c/p\u003e\n\u003cp\u003eSo first run the base image with the mounts that you want\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker run -v /mnt:/mnt -td --name containername baseimage /bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen copy whatever \u003ccode\u003esetup\u003c/code\u003e script you have and execute it on the running container\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker cp setup containername:/setup\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker exec -it containername /setup\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOnce the setup script finalizes, we can export the container filesystem into a file called \u003ccode\u003eimage.tar\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker export --output\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;image.tar\u0026#34;\u003c/span\u003e containername\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOnce we\u0026rsquo;ve exported the filesystem, we can get rid of the existing container\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker stop containername \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e docker rm containername\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow create a \u003ccode\u003eDockerfile\u003c/code\u003e with the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-dockerfile\" data-lang=\"dockerfile\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eFROM\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e scratch\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eADD\u003c/span\u003e image.tar /\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eCMD\u003c/span\u003e [\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;bin/bash\u0026#34;\u003c/span\u003e]\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow you can create the image by building the Dockerfile\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocker build -t finalimagename .\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.12.26",
    "tags": ["Linux","Containers"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/xephyr/",
    "url": "https://brandonrozek.com/blog/xephyr/",
    "title": "Nested X Sessions with Xephyr",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe X Window System was designed at a time when applications that ran on your computer were assumed to be trusted. Therefore there are minimal restrictions in place to prevent applications from grabbing images of whats on another applications screens. This feature as you might imagine is quite useful for screen clipper applications.\u003c/p\u003e\n\u003cp\u003eNow I know that we\u0026rsquo;re slowly moving towards Wayland on the Linux desktop, but X11 is still a reality even in my KDE Neon version that\u0026rsquo;s based on Ubuntu 18.04.\u003c/p\u003e\n\u003cp\u003eSo in the meantime, we can put our untrusted applications into a nested X server using \u003ccode\u003eXephyr\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFirst, we need to find an empty display variable. Chances are this would be \u003ccode\u003e:1\u003c/code\u003e for you. Though if you have multiple of these nested X-sessions running, then this script will be handy.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e((\u003c/span\u003eDNum\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e ; DNum \u0026lt;\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e ; DNum++\u003cspan style=\"color:#f92672\"\u003e))\u003c/span\u003e ; \u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e -e /tmp/.X11-unix/X$DNum \u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e break\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAt the end, \u003ccode\u003eDNum\u003c/code\u003e will be set to an empty display variable.\u003c/p\u003e\n\u003cp\u003eThen to start your nested X session,\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eXephyr :$DNum \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e        -extension MIT-SHM \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e        -extension XTEST \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e        -nolisten tcp \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e        -screen 1920x1080 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e        -resizeable \u0026amp;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf we\u0026rsquo;re going to be using this nested X server for one application, then I recommend using the \u003ccode\u003eratpoison\u003c/code\u003e window manager as it will make the application full screen and supports the resizing of the Xephyr window.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eratpoison -d :$DNum \u0026amp;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFinally, you should set your display variable so that any future GUI applications you execute from the terminal will show up in your new Xephyr window.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDISPLAY\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e:$DNum\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow if you want to run multiple applications, you\u0026rsquo;re likely better off using your native window manager.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Set the display before starting the window manager\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDISPLAY\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e:$DNum\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ex-window-manager \u0026amp;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOf course you can replace ratpoison and your native x-window manager with any other window manager of your choice. It actually makes it a great way to test different environments.\u003c/p\u003e\n",
    "date_published": "2019.12.25",
    "tags": ["X11"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/composesystemd/",
    "url": "https://brandonrozek.com/blog/composesystemd/",
    "title": "Ensuring Docker Compose Startup with Systemd",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve been having trouble getting some docker containers such as \u003ccode\u003enginx\u003c/code\u003e to start automatically on bootup, even with the \u003ccode\u003erestart: always\u003c/code\u003e flag.\u003c/p\u003e\n\u003cp\u003eTo compensate, I wrote a small systemd script and enabled it on startup.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e/etc/systemd/system/docker-compose.service\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Unit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDescription\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eDocker Compose Application Service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRequires\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enetwork.target docker.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eAfter\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003enetwork.target docker.service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Service]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eType\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eoneshot\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eUser\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ebrandonrozek\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eGroup\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003ebrandonrozek\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eRemainAfterExit\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eyes\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWorkingDirectory\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/home/brandonrozek/docker/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStart\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/docker-compose up -d --force-recreate\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eExecStop\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e/usr/bin/docker-compose down\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eTimeoutStartSec\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003e[Install]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWantedBy\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003emulti-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo enable on startup\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl enable docker-compose\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo start now\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl start docker-compose\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.12.16",
    "tags": ["Linux","Containers"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/traefiknginx/",
    "url": "https://brandonrozek.com/blog/traefiknginx/",
    "title": "How to use Nginx under Traefik",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve been enjoying Traefik for its auto-discovery of containers. The only problem is that for a couple containers such as Plex and HomeAssistant I have host networking enabled. This usually results in Traefik failing to forward the traffic properly.\u003c/p\u003e\n\u003cp\u003eHaving more fine grained control is exactly what Nginx is for!  I don\u0026rsquo;t want to switch my whole setup to Nginx since that would be a lot of configuration files for every docker container. But I think having configuration files for containers that use host networking is manageable.\u003c/p\u003e\n\u003cp\u003eIn your docker-compose file first make sure that Traefik is disabled for containers that use host networking by adding the label \u003ccode\u003etraefik.enable=false\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003ehomeassistant\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eimage\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ehomeassistant/home-assistant\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003econtainer_name\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ehomeassistant\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003ehostname\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ehomeassistant\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003enetwork_mode\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ehost\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eenvironment\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003ePUID=1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003ePGID=1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003evolumes\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003e/Volumes/homeassistant/config:/config\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003erestart\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ealways\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elabels\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      - \u003cspan style=\"color:#ae81ff\"\u003etraefik.enable=false\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen add a new section for \u003ccode\u003enginx\u003c/code\u003e adding the domains that you wish it to manage in the labels\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003enginx\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#f92672\"\u003eimage\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003elinuxserver/nginx\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#f92672\"\u003econtainer_name\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003enginx\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#f92672\"\u003eenvironment\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   - \u003cspan style=\"color:#ae81ff\"\u003ePUID=1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   - \u003cspan style=\"color:#ae81ff\"\u003ePGID=1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#f92672\"\u003evolumes\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   - \u003cspan style=\"color:#ae81ff\"\u003e/Volumes/nginx/config:/config\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#f92672\"\u003erestart\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ealways\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#f92672\"\u003elabels\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   - \u003cspan style=\"color:#ae81ff\"\u003etraefik.http.routers.my-container.rule=Host(`plex.example.com`,`homeassistant.example.com`)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow on the host system add the configuration files for nginx to consume. Example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-nginx\" data-lang=\"nginx\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eserver\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elisten\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e[::]:80\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eserver_name\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eplex.example.com\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003elocation\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e/\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_pass\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003ehttp://homelanip:32400\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_http_version\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e.1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eUpgrade\u003c/span\u003e $http_upgrade;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eConnection\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;upgrade\u0026#39;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_set_header\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003eHost\u003c/span\u003e $host;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003eproxy_cache_bypass\u003c/span\u003e $http_upgrade;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSimilarly add a configuration file for \u003ccode\u003ehomeassistant\u003c/code\u003e or any other service you have.\u003c/p\u003e\n\u003cp\u003eNow for this example, the subdomains \u003ccode\u003eplex.example.com\u003c/code\u003e and \u003ccode\u003ehomeassistant.example.com\u003c/code\u003e are managed by Nginx.\u003c/p\u003e\n\u003cp\u003eTo finish it off, \u003ccode\u003edocker-compose start nginx\u003c/code\u003e.\u003c/p\u003e\n",
    "date_published": "2019.12.16",
    "tags": ["Nginx"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/coredns/",
    "url": "https://brandonrozek.com/blog/coredns/",
    "title": "Setting up coredns and Zone Files",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eDomain names are the easiest way for a reverse proxy to split up services in a homelab. Since I\u0026rsquo;m going full docker-compose in my homelab, I decided to use \u003ccode\u003ecoredns\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThe server I\u0026rsquo;m hosting my services on runs Ubuntu 18.04 at the time of writing. This verison of Ubuntu comes packaged with their own DNS resolver \u003ccode\u003esystemd-resolved\u003c/code\u003e which runs on port 53. Therefore in order to stop this, we need to edit \u003ccode\u003e/etc/systemd/resolved.conf\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDNS\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e127.0.0.1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eFallbackDNS\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDomains\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e~.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDNSStubListener\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003eno\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou might be wondering about the \u003ccode\u003e~.\u003c/code\u003e option in Domains. From the \u003ca href=\"https://wiki.archlinux.org/index.php/Systemd-resolved\"\u003eArch Wiki\u003c/a\u003e, \u0026ldquo;Without the \u003ccode\u003eDomains=~.\u003c/code\u003e option, systemd-resolved might use the per-link DNS servers.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eYou also need to remove the already existing \u003ccode\u003e/etc/resolv.conf\u003c/code\u003e file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo rm /etc/resolv.conf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow to setup \u003ccode\u003ecoredns\u003c/code\u003e I used the following docker-compose snippet:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003ecoredns\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eimage\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003ecoredns/coredns\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003ecommand\u003c/span\u003e: -\u003cspan style=\"color:#ae81ff\"\u003econf coredns-config/Corefile\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eports\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003e53\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e53\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e/udp\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003evolumes\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    - \u003cspan style=\"color:#ae81ff\"\u003e/Volumes/coredns/coredns-config:/coredns-config/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNoticed that I mounted the configuration directory in a volume. \u003ccode\u003ecoredns\u003c/code\u003e is managed by a configuration file. This would be located under \u003ccode\u003e/Volumes/coredns/coredns-config/Corefile\u003c/code\u003e in the host system.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s say we own the domain example.com and want subdomains of that to point to different to our server. To do that, we need to define a zonefile for that domain name. We will save that under \u003ccode\u003e/Volumes/coredns/coredns-config/homelab.db\u003c/code\u003e in our host system. Every other domain we will either pass to a public DNS server like Google\u0026rsquo;s \u003ccode\u003e8.8.8.8\u003c/code\u003e or you can even use a PiHole.\u003c/p\u003e\n\u003cp\u003eExample Corefile:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eexample.com:53 {\n    file /coredns-config/homelab.db\n    log\n    errors\n}\n\n.:53 {\n    log\n    errors\n    forward . 8.8.8.8\n    cache\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eI do recommend that whatever domain you do use for your homelab is one you own. Otherwise you might name conflict with a public service that you would want to access in the future.\u003c/p\u003e\n\u003cp\u003eThe \u003ccode\u003ecache\u003c/code\u003e directive will cache results from the other DNS provider for quicker delivery at a later point.\u003c/p\u003e\n\u003cp\u003eThe zonefile \u003ccode\u003ehomelab.db\u003c/code\u003e could look like the following:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$TTL    3600\n$ORIGIN example.com.\n@                    IN  SOA   ns.example.com. contact.example.com. (\n                                               2019121301 ; serial\n                                               1d ; refresh\n                                               2h ; retry\n                                               4w ; expire\n                                               1h ; nxdomain ttl\n                                              )\n                     IN  NS  ns.example.com.\n@                    IN  A   192.168.1.10\n*                    IN  A   192.168.1.10\n@                    IN  A   10.10.100.10\n*                    IN  A   10.10.100.10\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eIn the zone file, \u003ccode\u003e@\u003c/code\u003e means whatever the value of \u003ccode\u003e$ORIGIN\u003c/code\u003e is and \u003ccode\u003e*\u003c/code\u003e is a wildcard meaning any subdomain. You can assign a local LAN address first and then a VPN ip address if you have one set up to access services remotely.\u003c/p\u003e\n\u003cp\u003eThe serial number needs only to be unique across SOA entries, and is conventionally the date and hour in which the entry was modified.\u003c/p\u003e\n\u003cp\u003eI have this setup since all my services are on one physical machine. If you want to split it up through many different machines, specify it by adding the subdomains in place of the \u003ccode\u003e*\u003c/code\u003e or \u003ccode\u003e@\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eAfter the \u003ccode\u003eCorefile\u003c/code\u003e and \u003ccode\u003ehomelab.db\u003c/code\u003e are made, the DNS server is all ready to be up and running. Just execute \u003ccode\u003edocker-compose up -d coredns\u003c/code\u003e and the DNS server will start happily responding to requests.\u003c/p\u003e\n\u003cp\u003eA good way to verify it works is to request an \u003ccode\u003eA\u003c/code\u003e record from your site and an external site.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edig @serverip example.com A\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edig @serverip google.com A\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.12.13",
    "tags": ["Linux","Networking","Containers"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/aspell/",
    "url": "https://brandonrozek.com/blog/aspell/",
    "title": "Spell check on the Terminal with aspell",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen I was working on my honors thesis, I realized that I needed a way to spell check the file. After a quick search, I stumbled upon \u003ccode\u003easpell\u003c/code\u003e which is a \u003ccode\u003ecurses\u003c/code\u003e based tool to provide an interactive way for dealing with misspellings. Try it out when you have the chance!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003easpell check file.md\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.12.10",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/rsa/",
    "url": "https://brandonrozek.com/blog/rsa/",
    "title": "RSA Cryptography",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003ch1 id=\"introduction\"\u003eIntroduction\u003c/h1\u003e\n\u003cp\u003eWhy do we need Cryptography? Well let\u0026rsquo;s say Bob is purchasing things from Alice over the internet. He wants to give Alice his credit card details to complete his transaction, but he feels uncomfortable with the thought that others can possibly read it as well. RSA is a cryptosystem that underlies HTTPS, and this gives Bob a sense of security that only Alice can see his message.\u003c/p\u003e\n\u003cp\u003eThe journey of RSA starts in 1976 when Whitfield Diffie and Martin Hellman published  a framework describing an asymmetric public-private key cryptosystem through four key steps:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eKey Generation\u003c/li\u003e\n\u003cli\u003eKey Distribution\u003c/li\u003e\n\u003cli\u003eEncryption with one-way function (using a public key)\u003c/li\u003e\n\u003cli\u003eDecryption (using a private key)\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eThey left the one-way function undefined, making it an open problem for the community. Ron \u003cstrong\u003eR\u003c/strong\u003eivest, Adi \u003cstrong\u003eS\u003c/strong\u003ehamir, and Leonard \u003cstrong\u003eA\u003c/strong\u003edleman at MIT took up this challenge. Rivest and Shamir were Computer Scientists trying to come up with this function while Adleman was the mathematician tasked with finding flaws in their discoveries. After working together to develop the RSA cryptosystem in 1977, Rivest, Shamir, and Adleman went on to found the organization RSA Data Security in 1982.\u003c/p\u003e\n\u003ch1 id=\"rsa-algorithm\"\u003eRSA Algorithm\u003c/h1\u003e\n\u003cp\u003eIn this section, we\u0026rsquo;ll look at the RSA algorithm through the lens of the four steps listed in the previous section.\u003c/p\u003e\n\u003ch4 id=\"key-generation\"\u003eKey Generation\u003c/h4\u003e\n\u003cp\u003eAlice needs to generate public and private keys through the following method:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eChoose two distinct prime integers $p$ and $q$.\u003c/li\u003e\n\u003cli\u003eCompute $n = pq$. The integer $n$ will be later released as part of the \u003cem\u003epublic key\u003c/em\u003e.\u003c/li\u003e\n\u003cli\u003eCalculate $\\phi(n)$ which by the Euler\u0026rsquo;s Totient Product of Primes property is $(p - 1)(q - 1)$.\u003c/li\u003e\n\u003cli\u003eChoose $e \\in \\mathbb{Z}$ such that $e$ and $\\phi(n)$ are relatively prime. The integer $e$ will also be released as part of the \u003cem\u003epublic key\u003c/em\u003e.\u003c/li\u003e\n\u003cli\u003eCalculate $d$, the multiplicative inverse of $e$ modulo $\\phi(n)$. The integer $d$ will be kept secret as the \u003cem\u003eprivate key\u003c/em\u003e.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch4 id=\"key-distribution\"\u003eKey Distribution\u003c/h4\u003e\n\u003cp\u003eFor Bob to send a message to Alice, he will need to know Alice\u0026rsquo;s public key $n$ and $e$. Bob, however, does not need to know Alice\u0026rsquo;s prime integers $p$ and $q$ that divide $n$. Nor does he need to know the private key $d$.\u003c/p\u003e\n\u003ch4 id=\"encryption\"\u003eEncryption\u003c/h4\u003e\n\u003cp\u003eLet $m \\in \\mathbb{Z}_n$ be the message that Bob wants to send. Bob calculates this number from his message using an agreed-upon reversible algorithm. The ciphertext or encrypted block $c$ is computed by, $c \\equiv m^e$ (mod $n$). Bob then sends the ciphertext to Alice.\u003c/p\u003e\n\u003ch4 id=\"decryption\"\u003eDecryption\u003c/h4\u003e\n\u003cp\u003eAlice receives this ciphertext $c$ from Bob and then can recover the original message $m$ by computing $c^d$ modulo $n$. How is this possible? Well recall that $c^d \\equiv (m^e)^d$. Then, $c^d \\equiv m^{ed}$ by exponent rules. Recall that $e$ and $d$ are multiplicative inverses modulo $\\phi(n)$. Therefore by definition, $ed = 1 + \\phi(m)t$ for some $t \\in \\mathbb{Z}$. Then,\u003c/p\u003e\n\u003cp\u003e\\begin{align*}\nc^d \u0026amp;\\equiv m^{1 + \\phi(n)t} \\\\\n\u0026amp;\\equiv m(m^{\\phi(n)})^t\n\\end{align*}\u003c/p\u003e\n\u003cp\u003eWell since $n$ is prime, by Euler\u0026rsquo;s Theorem $m^{\\phi(n)} \\equiv 1$ modulo $n$. Thus, $c^d \\equiv m \\cdot 1^t \\equiv m$ modulo $n$. Through this short proof, we can see that Alice successfully retrieves Bob\u0026rsquo;s message $m$ just by raising the ciphertext $c$ to the power of the private key $d$.\u003c/p\u003e\n\u003ch2 id=\"example\"\u003eExample\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s run through a short example of this algorithm with small integers. Let $p = 3, q = 5$. Then $n = pq = 15$. We then calculate $\\phi(n) = \\phi(p)\\phi(q) = 2(4) = 8$. An integer $e$ that is relatively prime to $\\phi(n)$ is $11$. Finally we compute the inverse of $e$ modulo $\\phi(n)$.\u003c/p\u003e\n\u003cp\u003e\\begin{align*}\n11d \u0026amp;\\equiv 1 \\text{ (mod $8$) } \\\\\n11d \u0026amp;\\equiv 33 \\text{ (mod $8$)} \\\\\nd \u0026amp;\\equiv 3 \\text{ (mod $8$)}\n\\end{align*}\u003c/p\u003e\n\u003cp\u003eAlice then gives Bob the integers $n = 15$ and $e = 11$. Bob can then encode his message (say $m = 12$) by raising $m$ to the power of $e$. Thus, $c = m^e = 12^{11} = 743008370688 \\equiv 3$ modulo 15. Alice can then recover $m = 12$ by raising $c = 3$ to the power of her private key $d$. $m \\equiv c^d \\equiv 3^3 \\equiv 27 \\equiv 12$ modulo $15$.\u003c/p\u003e\n\u003ch1 id=\"discussion\"\u003eDiscussion\u003c/h1\u003e\n\u003cp\u003eWell if it\u0026rsquo;s this simple to calculate, why is it secure? It is actually extremely difficult to factor a large prime number $n$. If $n$ can be factored into $p$ and $q$, then it is easy to compute $d \\equiv e^{-1}$ modulo $\\phi(pq)$. It is also really hard to efficiently solve for $m$ in the congruence $c \\equiv m^e$ modulo $n$. RSA Laboratories on March 1991 issued a challenge tasking the public to calculate prime factors of $n$ (called RSA numbers) of increasing digits. They offered a cash prize of up to $200,000 for solving RSA-2048 which is a 617 digit long number. As of the time of the presentation, the largest factored RSA number was RSA-768 (768 bits or 232 decimal digits) which was calculated over the span of two years. As of the time of this writing, towards the end of this year (2019) the largest known factored RSA number is actually RSA-240 (795 bits or 240 decimal digits). Currently the National Institute of Standards and Technology (NIST) recommends the use of 2048-bit keys for use in RSA. So for the time being, our communications are safe.\u003c/p\u003e\n",
    "date_published": "2019.12.10",
    "tags": ["Security"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/groupssimplified/",
    "url": "https://brandonrozek.com/blog/groupssimplified/",
    "title": "Simplifying the Definition of Algebraic Groups",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis post is inspired by the book \u0026ldquo;Term Rewriting \u0026amp; All That\u0026rdquo; by Franz Baader and Tobias Nipkow.\u003c/p\u003e\n\u003cp\u003eLet us have a set $G$ together with a binary operation $*$. We will use multiplicative notation throughout meaning $ab = a * b$. Let $x, y, z \\in G$. If $\\langle G , * \\rangle$ has the following properties:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e$(x y)z = x (y z)$\u003c/li\u003e\n\u003cli\u003e$ex = x$\u003c/li\u003e\n\u003cli\u003e$x^{-1} x = e$\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003efor some fixed $e \\in G$, then we say that $\\langle G, * \\rangle$ is a group.\u003c/p\u003e\n\u003cp\u003eWhen I was taking Abstract Algebra, we needed to also show that $xe = x$ and $xx^{-1} = e$ for an algebraic structure to be a group.\u003c/p\u003e\n\u003cp\u003eHowever, these can be derived by the prior properties.\u003c/p\u003e\n\u003ch3 id=\"prove-xx-1--e\"\u003eProve $xx^{-1} = e$\u003c/h3\u003e\n\u003cp\u003e\\begin{align*}\ne \u0026amp;= (xx^{-1})^{-1}(x x^{-1}) \\\\\n\u0026amp;= (xx^{-1})^{-1} (x (ex^{-1})) \\\\\n\u0026amp;= (xx^{-1})^{-1} (x ((x^{-1} x) x^{-1})) \\text{ \u0026mdash;\u0026ndash; (A)} \\\\\n\u0026amp;= (x x^{-1})^{-1} (x (x^{-1} x)x^{-1}) \\\\\n\u0026amp;= (x x^{-1})^{-1}((x x^{-1})xx^{-1}) \\\\\n\u0026amp;= (x x^{-1})^{-1} ((xx^{-1}) (x x^{-1})) \\\\\n\u0026amp;= ((x x^{-1})^{-1}(x  x^{-1})) (x x^{-1}) \\\\\n\u0026amp;= e(xx^{-1}) \\\\\n\u0026amp;= xx^{-1}\n\\end{align*}\u003c/p\u003e\n\u003ch3 id=\"prove-xe--x\"\u003eProve $xe = x$\u003c/h3\u003e\n\u003cp\u003eOnce we showed $xx^{-1} = e$, the proof of $xe = e$ is simple.\n\\begin{align*}\nx \u0026amp;= ex \\\\\n\u0026amp;= (xx^{-1})x \\\\\n\u0026amp;= x(x^{-1}x) \\\\\n\u0026amp;= xe\n\\end{align*}\u003c/p\u003e\n",
    "date_published": "2019.12.10",
    "tags": ["Math"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/videosandgifs/",
    "url": "https://brandonrozek.com/blog/videosandgifs/",
    "title": "Transforming Videos into GIFs for Slides",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI like using GIFs in Google Slides because it doesn\u0026rsquo;t require any interaction to start playing right away. Therefore, I looked at a couple resources to add a GIF from a video into my presentation. Of course this is all using one of my favorite terminal tools \u003ccode\u003effmpeg\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eFirst of all, I wanted to include a video of a bot playing Pong. The video was a little long, so I decided speed it up for a more time-lapse type feel.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://trac.ffmpeg.org/wiki/How%20to%20speed%20up%20/%20slow%20down%20a%20video\"\u003eFrom the FFMPEG website\u003c/a\u003e: To 10x the speed \u003ccode\u003effmpeg -i input.mkv -filter:v \u0026quot;setpts=0.1*PTS\u0026quot; output.mkv\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eYou just need to replace the \u003ccode\u003e0.1\u003c/code\u003e with the appropriate decimal number to change the speed.\u003c/p\u003e\n\u003cp\u003eGiphy has a great \u003ca href=\"https://web.archive.org/web/20200109095206/https://engineering.giphy.com/how-to-make-gifs-with-ffmpeg/\"\u003ewriteup\u003c/a\u003e describing the commands to use in order to make your GIF look nice.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003effmpeg -i inputvideo.mp4 -filter_complex \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;[0:v] palettegen\u0026#34;\u003c/span\u003e palette.png\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003effmpeg -i inputvideo.mp4 -i palette.png -filter_complex \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;[0:v][1:v] paletteuse\u0026#34;\u003c/span\u003e output.gif\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.12.04",
    "tags": ["Audio-Video"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/collabpandocbeamer/",
    "url": "https://brandonrozek.com/blog/collabpandocbeamer/",
    "title": "Collaborating on Beamer Pandoc Slides",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eRecently I\u0026rsquo;ve been making slides using Pandoc with Beamer. However, I came across the issue where I needed to collaborate with someone using already existing slides written in Markdown.\u003c/p\u003e\n\u003cp\u003eSadly, I don\u0026rsquo;t think this person (and most people for that matter) wants to edit slides in Markdown and use Pandoc. So I needed to convert this into another source material. My original plan was to convert this into a Powerpoint and put it into Google Slides. When I went to convert it using Pandoc, it stripped my math typing and theme. Unfortunately, that wasn\u0026rsquo;t going to work for me.\u003c/p\u003e\n\u003cp\u003eThe solution I ultimately ended using was the following.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eConvert the Markdown into Latex\u003c/li\u003e\n\u003cli\u003eUpload the latex document into Overleaf\u003c/li\u003e\n\u003cli\u003eUse the share feature in Overleaf\u003c/li\u003e\n\u003c/ol\u003e\n",
    "date_published": "2019.11.22",
    "tags": null,
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/wormhole/",
    "url": "https://brandonrozek.com/blog/wormhole/",
    "title": "Wormhole: Easiest way to transfer files between two linux machines",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eA dead simple way to send files between two Linux machines not on the same network is to use a utility called \u003ca href=\"https://github.com/warner/magic-wormhole\"\u003eMagic Wormhole\u003c/a\u003e. It is typically included in the standard repositories and is so simple the this blog post is going to end soon.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSend a file:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewormhole send filename \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis will generate a code for you to share like \u003ccode\u003e6-speculate-allow\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eReceive a file:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewormhole receive 6-speculate-allow\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDone.\u003c/p\u003e\n\u003cp\u003eYou can also rest in the fact that your data is secured by a \u003ca href=\"https://en.wikipedia.org/wiki/Password-authenticated_key_agreement\"\u003ePassword-Authenticated Key Agreement (PAKE)\u003c/a\u003e.\u003c/p\u003e\n",
    "date_published": "2019.11.20",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/ngrok/",
    "url": "https://brandonrozek.com/blog/ngrok/",
    "title": "Ngrok",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLet\u0026rsquo;s say you want to spin up a quick demo for a client and you don\u0026rsquo;t want to use a VPS, and they can\u0026rsquo;t access your laptop through the network.\u003c/p\u003e\n\u003cp\u003eThe easiest way I\u0026rsquo;ve known for the past few years to allow another person to access a specific port on your machine is through \u003ca href=\"https://ngrok.com/\"\u003engrok\u003c/a\u003e. Ngrok is nice because not only do they offer a free plan, but they also offer paid plans. This means that you can trust that it\u0026rsquo;ll at least be in business for a little while longer ;)\u003c/p\u003e\n\u003ch2 id=\"the-process\"\u003eThe Process\u003c/h2\u003e\n\u003cp\u003eYou\u0026rsquo;ll need to sign up for an account first. I was then going to write about some of the following steps, but \u003ccode\u003engrok\u003c/code\u003e has a really nice quad chart when you login\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/0932485094325.png\" alt=\"steps\"\u003e\u003c/p\u003e\n\u003cp\u003eJust note that in step (3) you will actually have a random sequence of characters after \u003ccode\u003eauthtoken\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eSomething else you might want to know is how to enable TLS support, luckily that\u0026rsquo;s a simple command line argument.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003engrok http -bind-tls\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003etrue port\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.11.20",
    "tags": ["Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/wireguardvpn/",
    "url": "https://brandonrozek.com/blog/wireguardvpn/",
    "title": "Wireguard VPN",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eHaving some sort of VPN solution has always been a necessity for me. Whether it\u0026rsquo;s back in the day where LAN games where the rage and I wanted to play it with my distant friends, or nowadays when I need to be able to access my Desktop running simulations behind my home LAN.\u003c/p\u003e\n\u003cp\u003eThis blog post is going to describe how I settled on Wireguard VPN as my preferred solution and how I use it to create a small secured network.\u003c/p\u003e\n\u003cp\u003eKeep note that I am not talking about a VPN that masks your internet traffic. For that, look up a VPN provider such as Private Internet Access or ProtonVPN.\u003c/p\u003e\n\u003ch2 id=\"about-wireguard\"\u003eAbout Wireguard\u003c/h2\u003e\n\u003cp\u003eWireguard is a point-to-point protocol. This means that you will be exchanging public and private keys between two clients and only those two clients will communicate with one another.\u003c/p\u003e\n\u003cp\u003eIn a way this makes it a lot simpler than other VPN solutions like OpenVPN where you have to set up a key server. That doesn\u0026rsquo;t mean, however, that we cannot build upon this concept to create a secure network.\u003c/p\u003e\n\u003cp\u003eNow in order to create this network, we\u0026rsquo;ll need at least one publicly accessible through the Internet computer. I use a VPS instance on \u003ca href=\"https://www.digitalocean.com/\"\u003eDigitalOcean\u003c/a\u003e to act as my \u003cem\u003eserver\u003c/em\u003e.\u003c/p\u003e\n\u003ch2 id=\"key-generation\"\u003eKey Generation\u003c/h2\u003e\n\u003cp\u003eFirst you\u0026rsquo;ll want to get \u003ca href=\"https://www.wireguard.com/install/\"\u003eWireguard installed\u003c/a\u003e, and then create public-private keys for both the \u003cem\u003eserver\u003c/em\u003e and the \u003cem\u003eclient\u003c/em\u003e computers.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOn each machine:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd /etc/wireguard\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo umask \u003cspan style=\"color:#ae81ff\"\u003e077\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo wg genkey | tee privatekey | wg pubkey \u0026gt; publickey\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eOn the server:\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eEdit \u003ccode\u003e/etc/wireguard/wg0.conf\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003eInterface]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eAddress = 10.10.100.1/24\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eSaveConfig = false\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eListenPort = 51826\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003ePrivateKey = \u0026lt;server_private_key\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003ePeer]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003ePublicKey = \u0026lt;client1_public_key\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eAllowedIPs = 10.10.100.2/32\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003ePeer]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003ePublicKey = \u0026lt;client2_public_key\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eAllowedIPs = 10.10.100.3/32\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou might be wondering why we have \u003ccode\u003e/24\u003c/code\u003e in the Address field but \u003ccode\u003e/32\u003c/code\u003e in the AllowedIPs field.\u003c/p\u003e\n\u003cp\u003eThis is because we want our address to be in the \u003ccode\u003e/24\u003c/code\u003e subnet, but we only want that specific IP to be able to connect via that specific public key.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eFirewall Rules\u003c/strong\u003e\nFirst you want to make sure that the port you specified is open on your firewall\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo ufw allow \u003cspan style=\"color:#ae81ff\"\u003e51826\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNext you\u0026rsquo;ll want to allow routing and any traffic to happen within that VPN interface.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo ufw route allow in on wg0 out on wg0\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow to have your server route traffic between the different clients connected to it, you need to enable IPv4 forwarding.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eFor current session:\u003c/em\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esysctl -w net.ipv4.ip_forward\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cem\u003eFor persistence across reboots:\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eEdit \u003ccode\u003e/etc/sysctl.d/99-sysctl.conf\u003c/code\u003e to make sure \u003ccode\u003enet.ipv4.ip_forward=1\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOn the client machines:\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eEdit \u003ccode\u003e/etc/wireguard/wg0.conf\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003eInterface]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eAddress = 10.10.100.x/32\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003ePrivateKey = \u0026lt;client_private_key\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003ePostUp = ping -c1 10.10.100.1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eDNS = 10.10.100.y\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e# Include only if relevant\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003ePeer]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003ePublicKey = \u0026lt;server_public_key\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eEndpoint = public_ip_or_domain:51826\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eAllowedIPs = 10.10.100.0/24\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003ePersistentKeepalive = 21\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReplace \u003ccode\u003ex\u003c/code\u003e with a unique value per client. This configuration file has the client establish a connection to the server, send a packet via ping to initiate the connection and then try to persistently keep it alive.\u003c/p\u003e\n\u003cp\u003eThe DNS server is helpful if you have a DNS server running in that private network to access local resources. If you don\u0026rsquo;t have an existing DNS server in the network, do not include that line. Also if you receive any errors in the future, you might need to make sure \u003ccode\u003eresolvconf\u003c/code\u003e is a command on your system.\u003c/p\u003e\n\u003cp\u003eIf you want you can also allow traffic within your trusted VPN network.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo ufw allow in on wg0 out on wg0\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eOn all machines:\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eHave the Wireguard service start at boot\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl enable wg-quick@wg0\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo systemctl start wg-quick@wg0\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAlternatively you can add the profile to NetworkManager for it to manage.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo nmcli connection import type wireguard file /etc/wireguard/wg0.conf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd enjoy a fully secure routable network!\u003c/p\u003e\n\u003ch2 id=\"potential-errors\"\u003ePotential Errors\u003c/h2\u003e\n\u003cp\u003eWireguard depends on \u003ccode\u003eresolvconf\u003c/code\u003e. I noticed that on Kubuntu 19.10, that is not installed by default. If you see a error message involving \u003ccode\u003eresolvconf\u003c/code\u003e chances are you need to install that package.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt install resolvconf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.11.20",
    "tags": ["Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/manpandoc/",
    "url": "https://brandonrozek.com/blog/manpandoc/",
    "title": "Man Pages with Pandoc",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://emar10.dev\"\u003eEthan Martin\u003c/a\u003e recently showed off the Pandoc tool to me. In case you don\u0026rsquo;t know, Pandoc is a swiss-army knife of markup languages. It allows you to easily convert from one markup language to another.\u003c/p\u003e\n\u003cp\u003eThis got me thinking my love for Markdown as the lowest common denominator for my notes. I can write markdown files for my website, for presentations, and output it into PDF and Word. Of course you don\u0026rsquo;t get to have the fine control as you do with Latex, but with document classes, you can at least theme it any way you like!\u003c/p\u003e\n\u003cp\u003eNow to focus this blog post, I do want to share one really cool feature I found out about Pandoc. It can create man pages. I thought of this as a great way to create documentation for a software project. I\u0026rsquo;ll give you an example to kick things off.\u003c/p\u003e\n\u003cp\u003eNote: The content is stolen from the \u003ccode\u003egit\u003c/code\u003e man page.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-markdown\" data-lang=\"markdown\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e---\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etitle: git\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esection: 1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eheader: Git Manual\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e---\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e# NAME\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  git - the stupid content tracker\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e# SYNOPSIS\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  git \u003cspan style=\"color:#e6db74\"\u003e`[--version] [--help]`\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e# DESCRIPTION\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  Git is a fast, scalable, distributed revision control system with an unusually rich command set that provides both high-level operations and full access to internals.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  See \u003cspan style=\"font-weight:bold\"\u003e**gittutorial**\u003c/span\u003e(7) to get started, then see \u003cspan style=\"font-weight:bold\"\u003e**giteveryday**\u003c/span\u003e(7) for a useful minimum set of commands. The \u003cspan style=\"font-weight:bold\"\u003e**Git User’s Manual**\u003c/span\u003e[1] has a more in-depth introduction.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  After you mastered the basic concepts, you can come back to this page to learn what commands Git offers. You can learn more about individual Git commands with \u0026#34;git help command\u0026#34;. \u003cspan style=\"font-weight:bold\"\u003e**gitcli**\u003c/span\u003e(7) manual page gives you an overview of the command-line command syntax.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  A formatted and hyperlinked copy of the latest Git documentation can be viewed at \u003cspan style=\"font-weight:bold\"\u003e**https://git.github.io/htmldocs/git.html**\u003c/span\u003e.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e# OPTIONS\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  --version\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Prints the Git suite version that the git program came from.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  --help\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Prints the synopsis and a list of the most commonly used commands. If the option --all or -a is given then all available commands are printed. If a Git command is named this option will bring up the manual page for that command.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  Other options are available to control how the manual page is displayed. See git-help(1) for more information, because git --help ...  is converted internally into git help ....\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen execute\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epandoc -s -t man -o git.1 git.md\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHere\u0026rsquo;s what the command means: \u0026ldquo;Run \u003ccode\u003epandoc\u003c/code\u003e on standalone mode converting \u003ccode\u003egit.md\u003c/code\u003e into a man page called \u003ccode\u003egit.1\u003c/code\u003e\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eTo see the results run \u003ccode\u003eman ./git.1\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/1573183066061.png\" alt=\"1573183066061\"\u003e\u003c/p\u003e\n",
    "date_published": "2019.11.07",
    "tags": ["Documentation"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/blogworkflow/",
    "url": "https://brandonrozek.com/blog/blogworkflow/",
    "title": "Blog Workflow",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eYou may be curious on how I create content for this blog. There\u0026rsquo;s pros and cons to my approach. One pro is that it\u0026rsquo;s relatively easy for me to crank something out and upload it to my site. The downside is that it\u0026rsquo;s not as easily customizable.\u003c/p\u003e\n\u003cp\u003eI use Hugo as my static site generator. It works with Markdown files. I begin every blog post by typing\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehugo new blog/title.md\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis creates a file called \u003ccode\u003etitle.md\u003c/code\u003e in \u003ccode\u003econtent/blog\u003c/code\u003e with front-matter yaml information such as the title and the datetime that I created the post.\u003c/p\u003e\n\u003cp\u003eI then use my favorite Markdown editor \u003ccode\u003etypora\u003c/code\u003e to open up the file in the background.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etypora content/blog/title.md \u0026amp;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWrite some great words down, compile the website, then sync it to my site!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehugo\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ersync -Paz --delete public/ brandonrozek.com:/path/to/site/folder\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.10.28",
    "tags": ["Hugo","Blogging"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pythontyping/",
    "url": "https://brandonrozek.com/blog/pythontyping/",
    "title": "Python Typing",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThere\u0026rsquo;s a typing module built right into Python that you can use on your applications. Sobolevn write a great \u003ca href=\"https://sobolevn.me/2019/01/simple-dependent-types-in-python\"\u003eblog post\u003c/a\u003e about it. One thing that threw me off at first is that if you add type annotations and then run python like you normally would, it would act as if the annotations weren\u0026rsquo;t there.\u003c/p\u003e\n\u003cp\u003eWhy use type annotations then?\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eIt enhances your internal docs. VS Code and other editors pick this up and show it to the user in their IDE.\u003c/li\u003e\n\u003cli\u003eYou can use \u003ccode\u003emypy\u003c/code\u003e to perform type checking for you.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eI go back and forth with type checking in Python, but I do think that forcing yourself to follow type safety makes you a better programmer.\u003c/p\u003e\n",
    "date_published": "2019.10.28",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/abstractdef/",
    "url": "https://brandonrozek.com/blog/abstractdef/",
    "title": "Useful Abstract Definitions",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003ch2 id=\"chapter-17\"\u003eChapter 17\u003c/h2\u003e\n\u003cp\u003eBy a \u003cstrong\u003ering\u003c/strong\u003e we mean a set $A$ with operations called addition and multiplication which satisfy the following axioms:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e$A$ with addition alone is an abelian group\u003c/li\u003e\n\u003cli\u003eMultiplication is associative\u003c/li\u003e\n\u003cli\u003eMultiplication is distributive over addition\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eSince $\\langle A, + \\rangle$ is an abelian group, there is in $A$ a neutral element for addition. This is called the \u003cstrong\u003ezero\u003c/strong\u003e element. Also, every element has an additive inverse called its \u003cstrong\u003enegative\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eIf multiplication in a ring $A$ is commutative then we say that $A$ is a \u003cstrong\u003ecommutative ring\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eIf a ring $A$ has a neutral element for multiplication then we say that the neutral element is the \u003cstrong\u003eunity\u003c/strong\u003e of $A$.\u003c/p\u003e\n\u003cp\u003eIf $A$ is a ring with unity, there may be elements in $A$ which have a multiplicative inverse. Such elements are said to be \u003cstrong\u003einvertible\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eIf $A$ is a commutative ring with unity in which every nonzero element is invertible, then $A$ is called a \u003cstrong\u003efield\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eIn any ring, a nonzero element $a$ is called a \u003cstrong\u003edivisor of zero\u003c/strong\u003e if there is a nonzero element $b$ in a ring such that the product $ab$ or $ba$ is equal to zero.\u003c/p\u003e\n\u003cp\u003eAn \u003cstrong\u003eintegral domain\u003c/strong\u003e is defined to be a commutative ring with unity that has the cancellation property. Another way of saying this is that an integral domain is a commutative ring with unity and has no zero divisors.\u003c/p\u003e\n\u003ch2 id=\"chapter-18\"\u003eChapter 18\u003c/h2\u003e\n\u003cp\u003eLet $A$ be a ring, and $B$ be a nonempty subset of $A$. $B$ is called a \u003cstrong\u003esubring\u003c/strong\u003e if it satisfies the following properties:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eClosed with respect to addition\u003c/li\u003e\n\u003cli\u003eClosed with respect to negatives\u003c/li\u003e\n\u003cli\u003eClosed with respect to multiplication\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eLet $B$ be a subring of $A$. We call $B$ an \u003cstrong\u003eideal\u003c/strong\u003e of $A$ if $xb, bx \\in B$ for all $b \\in B$ and $x \\in A$.\u003c/p\u003e\n\u003cp\u003eThe \u003cstrong\u003eprinciple ideal generated by $a$\u003c/strong\u003e, denoted $\\langle a \\rangle$ is the subring defined by fixing an element $a$ in a subring $B$ of $A$ and multiplying all elements of $A$ by $a$.\n$$\n\\langle a \\rangle = { xa : x \\in A }\n$$\nA \u003cstrong\u003ehomomorphism\u003c/strong\u003e from a ring $A$ to a ring $B$ is a function $f : A \\to B$ satisfying the identities:\n$$\nf(x_1 + x_2 ) = f(x_1) + f(x_2) \\\nf(x_1x_2) = f(x_1)f(x_2)\n$$\nIf there is a homomorphism from $A$ onto $B$, we call $B$ a \u003cstrong\u003ehomomorphic image\u003c/strong\u003e of $A$.\u003c/p\u003e\n\u003cp\u003eIf $f$ is a homomorphism from a ring $A$ to a ring $B$, the \u003cstrong\u003ekernel\u003c/strong\u003e of $f$ is the set of all the elements of $A$ which are carried by $f$ onto the zero element of $B$. In symbols, the kernel of $f$ is the set\n$$\nK = {x \\in A: f(x) = 0}\n$$\nIf $A$ and $b$ are rings, an \u003cstrong\u003eisomorphism\u003c/strong\u003e from $A$ to $B$ is a homomorphism which is a one-to-one correspondence from $A$ to $B$. In other words, it is injective and surjective homomorphism.\u003c/p\u003e\n\u003cp\u003eIf there is an isomorphism from $A$ to $B$ we say that $A$ is \u003cstrong\u003eisomorphic\u003c/strong\u003e to $B$, and this fact is expressed by writing $A \\cong B$.\u003c/p\u003e\n\u003ch2 id=\"chapter-19\"\u003eChapter 19\u003c/h2\u003e\n\u003cp\u003eLet $A$ be a ring, and $J$ an ideal of $A$. For any element $a \\in A$, the symbol $J + a$ denotes the set of all sums $j + a$ as $a$ remains fixed and $j$ ranges over $J$. That is,\n$$\nJ + a = {j + a : j \\in J}\n$$\n$J + a$ is called a \u003cstrong\u003ecoset\u003c/strong\u003e of $J$ in $A$.\u003c/p\u003e\n\u003cp\u003eNow think of the set which contains all cosets of $J$ in $A$. This set is conventionally denoted by $A / J$ and reads $A$ mod $J$. Then, $A / J$ with coset addition and multiplication is a ring.\u003c/p\u003e\n\u003cp\u003eAn ideal $J$ of a commutative ring is said to be a \u003cstrong\u003eprime ideal\u003c/strong\u003e if for any two elements $a$ and $b$ in the ring,\n$$\nab \\in J \\implies a \\in J \\text{ or } b \\in J\n$$\n\u003cstrong\u003eTheorem:\u003c/strong\u003e If $J$ is a prime ideal of a community ring with unity $A$, then the quotient ring $A / J$ is an integral domain.\u003c/p\u003e\n\u003cp\u003eAn ideal $J$ of $A$ with $J \\ne A$ is called a \u003cstrong\u003emaximal ideal\u003c/strong\u003e if there exists no proper ideal $K$ of $A$ such that $J \\subseteq K$ with $J \\ne K$.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTheorem:\u003c/strong\u003e If $A$ is a commutative ring with unity, then $J$ is a maximal ideal of $A$ iff $A/J$ is a field.\u003c/p\u003e\n",
    "date_published": "2019.10.27",
    "tags": ["Math"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/custom-python-repl/",
    "url": "https://brandonrozek.com/blog/custom-python-repl/",
    "title": "Custom Python REPL",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eAre you tired of importing the same libraries and setting up the same variables? Why not just create your own custom REPL? Now of course, we\u0026rsquo;re not going to do it from scratch, but instead utilize what Python already gives us.\u003c/p\u003e\n\u003cp\u003eKey Ingridient:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epython -i prompt.py\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis tells Python to run prompt.py and then take you into an interactive prompt.\u003c/p\u003e\n\u003cp\u003eNow let\u0026rsquo;s populate \u003ccode\u003eprompt.py\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/env python\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e favorite_libraries\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Welcome to your own custom REPL!\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Type help(\u0026#39;function_name\u0026#39;) to get a more detailed description on some of your own custom functions!\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehelp\u003c/span\u003e(function_name):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e function_name \u003cspan style=\"color:#f92672\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;help\u0026#34;\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Your very own help tool!\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        print(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Sorry we don\u0026#39;t have a help written for that yet :(\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.10.27",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/gitlens/",
    "url": "https://brandonrozek.com/blog/gitlens/",
    "title": "Git Lens: Superpower Git within VSCode",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eGit Lens is a great Visual Studio code extension. It annotates the line your cursor is on with the commit it came from. Extreemely useful for determining the reason that line of code is written or even who wrote it.\u003c/p\u003e\n\u003cp\u003eImages from the github page. (If they still exist when you read this)\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/docs/current-line-blame.png\" alt=\"Current Line Blame\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/eamodio/vscode-gitlens/master/images/docs/code-lens.png\" alt=\"Git Code Lens\"\u003e\u003c/p\u003e\n",
    "date_published": "2019.10.27",
    "tags": ["Git"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/notimplemented/",
    "url": "https://brandonrozek.com/blog/notimplemented/",
    "title": "NotImplemented in Python",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLet\u0026rsquo;s say you overwrite the \u003ccode\u003e__mul__\u003c/code\u003e operator in a class in Python, but you don\u0026rsquo;t want the function to be called for all kinds of input. You can specify the type by just returning \u003ccode\u003eNotImplemented\u003c/code\u003e for types you don\u0026rsquo;t want.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eA\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e __mul__(self, x):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e isinstance(x, A):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e NotImplemented\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e someOperation()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.10.27",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/livedoc/",
    "url": "https://brandonrozek.com/blog/livedoc/",
    "title": "Live Documentation",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis blog post is mostly for one of my teams in which I use Jupyter Notebooks for documentation. Perhaps after reading this post, you the reader can understand why it might be beneficial to use Jupyter Notebooks as a form of documentation.\u003c/p\u003e\n\u003ch2 id=\"why\"\u003eWhy?\u003c/h2\u003e\n\u003cp\u003eSo why Jupyter Notebooks?\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eFollows the literate programming approach. You can write text explaining a feature and then immediately show code and it\u0026rsquo;s result.\u003c/li\u003e\n\u003cli\u003eIt\u0026rsquo;s modifiable. If your user wants to play around with the documentation, the environment is set up for them to do so.\u003c/li\u003e\n\u003cli\u003eIt\u0026rsquo;s exportable. Let\u0026rsquo;s say another user doesn\u0026rsquo;t want to bother setting it up. Well it\u0026rsquo;s super simple to just export the notebook as a PDF and send that to them instead.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"setting-up\"\u003eSetting up\u003c/h2\u003e\n\u003cp\u003eJupyter Notebooks are part of the \u003ca href=\"https://jupyter.org/\"\u003eProject Jupyter\u003c/a\u003e suite of products. You can install it via a \u003ccode\u003epip\u003c/code\u003e package, but it is more commonly installed via the \u003ca href=\"https://www.anaconda.com/\"\u003eAnaconda Distribution\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eOnce you have that installed, run \u003ccode\u003ejupyter lab\u003c/code\u003e in the directory that you wish to execute code from. You might need to be in the \u003ccode\u003ebash\u003c/code\u003e shell for this to work since the installer modifies those environmental variables.\u003c/p\u003e\n",
    "date_published": "2019.09.27",
    "tags": ["Documentation"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/chirp/",
    "url": "https://brandonrozek.com/blog/chirp/",
    "title": "Chirp",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn the land of Ham Radio, you can program your radio with a very popular open source software called \u003ccode\u003echirp\u003c/code\u003e. For Ubuntu users to install it, it is recommended you use the PPA to keep up to date with radio software\u0026hellip;\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt-add-repository ppa:dansmith/chirp-snapshots\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo apt update \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e sudo apt install chirp-daily\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOnce you\u0026rsquo;re in, it is recommended that you download the configuration currently set on your radio and store it somewhere safe. You never know when you\u0026rsquo;ll want the Stock configuration again\u0026hellip;\u003c/p\u003e\n\u003cp\u003eThe way the Software works is that you can store a certain amount of channels into your radio. For the Baofeng F8-HP, 128 channels can be stored. The software gives you a table to manage them. The software is nice since you can cut and paste entries around.\u003c/p\u003e\n\u003cp\u003eWhen you\u0026rsquo;re importing frequencies from various sources, pay attention to what entry numbers you\u0026rsquo;re importing to. Otherwise you might overwrite another import you\u0026rsquo;ve made. I recommend adding the following types of frequencies to your radio\u0026hellip;\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eRepeaters: Can be added by using RepeaterBook\n\u003cul\u003e\n\u003cli\u003eI used proximity search. It takes a zip code and the radius in miles of what you want to import.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eLocal frequencies: You need a prenium account from Radio Reference in order to obtain these.\n\u003cul\u003e\n\u003cli\u003eThough honestly, a premium account is $15 for half a year. Not bad if you just go in once and download a bunch of frequencies for your area. Depending on where you live, your area might not have frequencies update all the time\u0026hellip;\u003c/li\u003e\n\u003cli\u003eImagine being stuck in traffic and getting to know what\u0026rsquo;s happening around you\u0026hellip;.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eNational Calling Frequencies\u003c/li\u003e\n\u003cli\u003eMURS/FRS/GMRS frequencies\u003c/li\u003e\n\u003cli\u003eMarine/Railroad frequencies (if you have space)\u003c/li\u003e\n\u003c/ul\u003e\n",
    "date_published": "2019.09.27",
    "tags": ["Linux","Amateur Radio"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/sshconfig/",
    "url": "https://brandonrozek.com/blog/sshconfig/",
    "title": "SSH Config",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eHave you ever gone through the hassle of having multiple public/private keys for accessing your remote servers? Before recently, I used to specify the identity file in all my transactions \u003ccode\u003essh -I ~/.ssh/private_key user@host\u003c/code\u003e but no longer! I discovered you can add the following to your \u003ccode\u003e~/.ssh/config\u003c/code\u003e to specify which key you want to use!\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eHost somename \n    Hostname someaddress\n    user usernameonserver\n    IdentityFile ~/.ssh/private_key\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eOf course you can add multiple of these entries in that file so that you can \u003ccode\u003essh\u003c/code\u003e without having to worry about explicitly using keys. Just keep in mind that the order of your entries matter.\u003c/p\u003e\n",
    "date_published": "2019.09.27",
    "tags": ["SSH"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/robustdd/",
    "url": "https://brandonrozek.com/blog/robustdd/",
    "title": "Robustly copying ISOs using dd",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cstrong\u003eNote:\u003c/strong\u003e \u003ca href=\"/blog/bmaptool-iso/\"\u003eCurrently I favor using \u003ccode\u003ebmaptool\u003c/code\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eThis blog post is going to assume that we\u0026rsquo;re writing to \u003ccode\u003e/dev/sdX\u003c/code\u003e. Please change this to whatever disk you\u0026rsquo;re actually trying to write to. I bear no responsibility if you accidentally write to your OS drives.\u003c/p\u003e\n\u003cp\u003eMake sure the disk you wish to write to is unmounted.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo umount /dev/sdX\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor good measure check \u003ccode\u003elsblk\u003c/code\u003e\u0026hellip;.\u003c/p\u003e\n\u003cp\u003eThen perform the writing operation\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dd bs\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e4M \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/path/to/distro.iso of\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/dev/sdX conv\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003efdatasync status\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eprogress\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003efdatasync\u003c/code\u003e will wait until the data is physically written into the drive before finishing.\u003c/p\u003e\n\u003cp\u003eOnce that operation is done we can check to see if it is performed properly. This step will only work for ISOs that have prepared a file similar to \u003ccode\u003emd5sum.txt\u003c/code\u003e that Ubuntu provides\u0026hellip;.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mount /dev/sdX /mnt\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd /mnt\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emd5sum -c md5sum.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHopefully you\u0026rsquo;ll see all \u003ccode\u003eOK\u003c/code\u003e and we\u0026rsquo;re good to go!\u003c/p\u003e\n",
    "date_published": "2019.09.27",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/digitalmodes/",
    "url": "https://brandonrozek.com/blog/digitalmodes/",
    "title": "Getting started with Digital Modes in Linux",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis blog post is going to describe what steps I took to be able to decode signals using digital modes. Hardware wise, you will either need a RTL-SDR receiver or a transceiver radio with a cable plugging into the computer\u0026rsquo;s soundcard.\u003c/p\u003e\n\u003ch2 id=\"software-required\"\u003eSoftware Required\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eGqrx (depends on GNU Radio)\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThis is if you are trying to work with RTL-SDR\u003c/li\u003e\n\u003cli\u003eIt acts as a AM/FM/CW/etc demodulator for the data sent by the equipment\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eFldigi\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThis is what decodes the protocol used to package the data\u003c/li\u003e\n\u003cli\u003eIt acts as a modem and provides capability to transmit data using a transceiver\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003ePavucontrol\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThis connects both Gqrx and Fldigi if using RTL-SDR\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eAs you can see, if you are only using a transceiver then you can skip the parts mentioning Gqrx and Pavucontrol\u003c/p\u003e\n\u003ch2 id=\"process\"\u003eProcess\u003c/h2\u003e\n\u003ch3 id=\"gqrx\"\u003eGqrx\u003c/h3\u003e\n\u003cp\u003eWhen you first open up Gqrx, it\u0026rsquo;ll ask you to configure the device. I have the RTL2832U, so if you have the same tool\u0026hellip;\u003c/p\u003e\n\u003cp\u003eSelect the profile partially labeled RTL2832U. If you want to look at frequencies between 0 and 28MHz then add to the device string \u0026ldquo;direct_samp=3\u0026rdquo;. Otherwise don\u0026rsquo;t add that string.\u003c/p\u003e\n\u003cp\u003eYou can keep the rest of the settings the same.\u003c/p\u003e\n\u003ch3 id=\"fldigi\"\u003eFldigi\u003c/h3\u003e\n\u003cp\u003eWhen you first open up Fldigi, it\u0026rsquo;ll ask you for our operator info.\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eField\u003c/th\u003e\n          \u003cth\u003edescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eStation Callsign\u003c/td\u003e\n          \u003ctd\u003eYour callsign\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eStation QTH\u003c/td\u003e\n          \u003ctd\u003eCity, State\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eStation Locator\u003c/td\u003e\n          \u003ctd\u003eYour grid code. (Can look this up on QRZ)\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eOperator Callsign\u003c/td\u003e\n          \u003ctd\u003eYour callsign\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eOperator Name\u003c/td\u003e\n          \u003ctd\u003eYour name\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eAntenna\u003c/td\u003e\n          \u003ctd\u003eYour antenna type\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eIn Audio-\u0026gt;Devices, make sure you select the physical device if you\u0026rsquo;re using a transceiver, otherwise select PulseAudio.\u003c/p\u003e\n\u003cp\u003eYou can keep the rest of the settings as the default.\u003c/p\u003e\n\u003ch3 id=\"pavucontrol\"\u003ePavucontrol\u003c/h3\u003e\n\u003cp\u003eFinally to link the two, run the following lines in bash to create a sink in order to push the audio from Gqrx to Fldigi.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epacmd load-module module-null-sink sink_name\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eMyLoopback\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epacmd update-sink-proplist MyLoopback device.description\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eMyLoopback\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epacmd load-module module-loopback sink\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eMyLoopback\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow open up \u003ccode\u003epavucontrol\u003c/code\u003e. On the Playback tab, change the output device of GQRX to be \u0026ldquo;MyLoopback\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eOn the Recording tab, change the input of fldigi to \u0026ldquo;Monitor of Null Output\u0026rdquo;\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eNow the software is sufficiently setup to be able to receive and decode signals. Best of luck finding things out there!\u003c/p\u003e\n\u003ch2 id=\"beginner-pitfalls\"\u003eBeginner Pitfalls\u003c/h2\u003e\n\u003cp\u003eSince I\u0026rsquo;m also a beginner, I will share what I got stuck on and how you can avoid it.\u003c/p\u003e\n\u003cp\u003eMake sure that you know the appropriate mode to demodulate the signal. I\u0026rsquo;ve had luck getting information from \u003ca href=\"https://www.sigidwiki.com/wiki/Signal_Identification_Guide\"\u003ehttps://www.sigidwiki.com/wiki/Signal_Identification_Guide\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e-\u0026gt; This needs to be configured on both Gqrx and Fldigi\u003c/p\u003e\n",
    "date_published": "2019.09.04",
    "tags": ["Amateur Radio"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/shutdownafterjob/",
    "url": "https://brandonrozek.com/blog/shutdownafterjob/",
    "title": "Shutdown After Job",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;m back to running longer jobs as part of my research. If I run a task overnight, I want to conserve energy and not keep it running after I finish. I suppose this would also apply to cloud billing, you want it to do the job and then stop.\u003c/p\u003e\n\u003cp\u003eThis technique will require you to have sudo privileges on the machine.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eChange user to root.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo su\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col start=\"2\"\u003e\n\u003cli\u003eRun job as regular user, write output to file, and then poweroff.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esu -u user task \u0026gt; output.txt \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e chown user:user output.txt \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e poweroff \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.08.30",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/localrepoiso/",
    "url": "https://brandonrozek.com/blog/localrepoiso/",
    "title": "Local Repo From Live Installer",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;m going to share my experience setting up a local repo from a CentOS live CD. These instructions should work similarly in REHL.\u003c/p\u003e\n\u003cp\u003eWhen working with off-network systems, it is often useful to have the base packages on the system after the installation process. Luckily the base packages are commonly included in the ISO installer.\u003c/p\u003e\n\u003cp\u003eFirst I\u0026rsquo;m going to assume that you\u0026rsquo;ve mounted the ISO at \u003ccode\u003e/mnt\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo mkdir -p /repos/local\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo rsync -Paz /mnt/Packages /repos/local/Packages\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo rsync -Paz /mnt/repodata /repos/local/repodata \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen create the file \u003ccode\u003e/etc/yum.repos.d/local.repo\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003elocal]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003ename=Local Repo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003ebaseurl=file:///repos/local/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003egpgcheck=0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eenabled=1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow you\u0026rsquo;re all set to go with the packages on the Live CD!\u003c/p\u003e\n",
    "date_published": "2019.08.30",
    "tags": null,
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/networkthroughput/",
    "url": "https://brandonrozek.com/blog/networkthroughput/",
    "title": "Network Throughput Testing",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI ended up upgrading the wiring in my place to CAT7 recently and I wanted to see if there was a noticeable performance difference to my previous cabling. This blog post won\u0026rsquo;t be a product comparison, but instead I\u0026rsquo;ll show how you can do network throughput testing at your own location.\u003c/p\u003e\n\u003cp\u003eThere is a great package called \u003ccode\u003eiperf3\u003c/code\u003e. It\u0026rsquo;s in most repositories under Linux, and binaries for Windows and macOS exist as well.\u003c/p\u003e\n\u003cp\u003eFor a more in depth tutorial \u003ca href=\"https://www.linode.com/docs/networking/diagnostics/install-iperf-to-diagnose-network-speed-in-linux/\"\u003echeck out this post from Linode\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eFor the 5 second spiel\u0026hellip;\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eOne one machine, start the server\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eiperf3 -s\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOn another machine connect to the server and begin testing the connection\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eiperf3 -c 192.168.0.2\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can also use the \u003ccode\u003e-t\u003c/code\u003e flag to specify the number of seconds you want to run the test for.\u003c/p\u003e\n",
    "date_published": "2019.08.30",
    "tags": ["Linux","Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/virtualizingwithclonezilla/",
    "url": "https://brandonrozek.com/blog/virtualizingwithclonezilla/",
    "title": "Virtualizing Environments with Clonezilla",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://clonezilla.org/\"\u003eClonezilla\u003c/a\u003e advertises itself as a disk cloning and backup utility. I\u0026rsquo;ve been starting to think of it as a little more than that. Let\u0026rsquo;s say that you go to a client site and they have a machine in production. Instead of messing around with their machine, you make a Clonezilla copy of it.  You can then take it back and place it on a computer you\u0026rsquo;re not using to play around with it.\u003c/p\u003e\n\u003cp\u003eNow even better, instead of using bare metal, what if you virtualize it? It\u0026rsquo;s essentially the same process as putting it on baremetal, except now you have a virtual copy that you can make clones of, backup, etc.\u003c/p\u003e\n\u003cp\u003eNow if the OS is looking for hardware that doesn\u0026rsquo;t exist, that might make life a bit harder\u0026hellip;.\u003c/p\u003e\n",
    "date_published": "2019.08.25",
    "tags": ["Virtualization"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/systemdwithpythonenvs/",
    "url": "https://brandonrozek.com/blog/systemdwithpythonenvs/",
    "title": "Systemd with Python environments",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIt took me some time to realize why I couldn\u0026rsquo;t start a project during startup. I then realized that it was because I was using a python virtual environment and didn\u0026rsquo;t tell systemd about it.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s how you can do so\u0026hellip;\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003eUnit]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eDescription=Start Service\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# After=network.target # If you want a python webserver...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003eService]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eType=Simple\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eUser=brandon\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eWorkingDirectory=/home/brandon/Development\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eExecStart=/bin/sh -c \u0026#34;. /home/brandon/Development/pythonenv/bin/activate; /home/brandon/Development/start\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003eInstall]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eWantedBy=multi-user.target\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.08.25",
    "tags": ["Linux","Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/bashflags/",
    "url": "https://brandonrozek.com/blog/bashflags/",
    "title": "Parsing CLI Flags in Bash",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI was creating a bash script and was looking around for a solution for parsing command line arguments. \u003ca href=\"https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash\"\u003eThis StackOverflow post\u003c/a\u003e has a variety of different solutions available. I want to describe my favorite of these posts.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://stackoverflow.com/users/115363/inanc-gumus\"\u003eInanc Gumus\u003c/a\u003e proposed the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e[[\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$#\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e -gt \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e]]\u003c/span\u003e; \u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e $1 in\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  -d|--deploy\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e deploy\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e$2\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e; shift;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  -u|--uglify\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e uglify\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e1;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  *\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e echo \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Unknown parameter passed: \u003c/span\u003e$1\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e; exit 1;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eesac\u003c/span\u003e; shift; \u003cspan style=\"color:#66d9ef\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Should deploy? \u003c/span\u003e$deploy\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Should uglify? \u003c/span\u003e$uglify\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLet me quickly describe what it does. While the number of arguments left to process is greater than zero\u0026hellip;.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eCheck to see if the argument matches any of the flags\n\u003cul\u003e\n\u003cli\u003eIf it does\u0026hellip;\n\u003cul\u003e\n\u003cli\u003eIf the flag requires an additional argument grab it. Then discard an argument.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eIf it doesn\u0026rsquo;t. Error out.\u003c/li\u003e\n\u003cli\u003eThen get rid of an argument.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eAt the end of the while loop, you would\u0026rsquo;ve evaluated all the arguments!\u003c/p\u003e\n",
    "date_published": "2019.08.06",
    "tags": ["Bash"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/sshlocalportforwarding/",
    "url": "https://brandonrozek.com/blog/sshlocalportforwarding/",
    "title": "SSH Local Port Forwarding",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThere are some services that I don\u0026rsquo;t want to run all the time which makes me not want to open a port for it. One good example of this is \u003ca href=\"https://jupyter.org/\"\u003eJupyter Notebooks\u003c/a\u003e. Therefore, what I sometimes do is run it locally and forward the port so that another machine can access it.\u003c/p\u003e\n\u003cp\u003eExample command:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003essh -L 8888:localhost:8888 -N 192.168.0.2\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003e-L\u003c/code\u003e flag allows you to specify the \u003ccode\u003elocalsocket:host:remotesocket\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e-N\u003c/code\u003e makes it so that it doesn\u0026rsquo;t execute any additional commands\u003c/p\u003e\n\u003cp\u003eThen finally you put the address of the machine you wish to connect to.\u003c/p\u003e\n",
    "date_published": "2019.08.06",
    "tags": ["SSH"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/archivingsites/",
    "url": "https://brandonrozek.com/blog/archivingsites/",
    "title": "Mirroring or Archiving an Entire Website",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI have several old Wordpress sites lying around that I would like to archive but not maintain anymore. Since I don\u0026rsquo;t intend to create any more content on these sites, we can use tools like \u003ccode\u003ewget\u003c/code\u003e to scrape an existing site and provide a somewhat \u003cem\u003eread-only\u003c/em\u003e copy of it. I say read-only not because we can\u0026rsquo;t edit it, but because it\u0026rsquo;s not in the original source format of the website.\u003c/p\u003e\n\u003cp\u003eThere have been several tackles to the problem:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://stackoverflow.com/questions/538865/how-do-you-archive-an-entire-website-for-offline-viewing#538878\"\u003ehttps://stackoverflow.com/questions/538865/how-do-you-archive-an-entire-website-for-offline-viewing#538878\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://web.archive.org/web/20190915143432/https://letswp.io/download-an-entire-website-wget-windows/\"\u003ehttps://letswp.io/download-an-entire-website-wget-windows/\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eAnd ultimately after consulting these resources I\u0026rsquo;ve came to the following command:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewget --mirror \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --convert-links \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --adjust-extension \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --page-requisites \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     --no-verbose \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e\u003c/span\u003e     https://url/of/web/site\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThere were other solutions in that stack overflow post, but something about the simplicity of \u003ccode\u003ewget\u003c/code\u003e appealed to me.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://sentenceworthy.com\"\u003eExample site I archived with this.\u003c/a\u003e\u003c/p\u003e\n",
    "date_published": "2019.08.02",
    "tags": ["Archive"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/copytoram/",
    "url": "https://brandonrozek.com/blog/copytoram/",
    "title": "Copy to RAM Please",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI bought a 1U server recently, and I forgot to purchase a SSD to actually hold data. Since I couldn\u0026rsquo;t hold my excitement to play with the server, I decided to load an operating system entirely into RAM and never turn it off for the meantime.\u003c/p\u003e\n\u003cp\u003eI browsed \u003ca href=\"https://en.wikipedia.org/wiki/List_of_Linux_distributions_that_run_from_RAM\"\u003eList of Linux Distributoins that run from RAM\u003c/a\u003e and found \u003ca href=\"https://www.slax.org/\"\u003eSlax\u003c/a\u003e a \u003ca href=\"https://www.debian.org/\"\u003edebian\u003c/a\u003e derivative.\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s not what I would use for my daily driver but in terms of doing this task its perfect. It loads up quickly and lets me perform nice basic user functions. It\u0026rsquo;s also based on debian so I have a whole repository of packages available I can install.\u003c/p\u003e\n\u003cp\u003eNow by default, if you\u0026rsquo;re on a USB it\u0026rsquo;ll try to run in a persistent mode, just press ESC in the boot splash to have the option to copy to ram.\u003c/p\u003e\n",
    "date_published": "2019.08.02",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/memoryerrorsgo/",
    "url": "https://brandonrozek.com/blog/memoryerrorsgo/",
    "title": "Memory Errors in Go",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI enjoy playing with Valgrind. Sometimes I view it as a game to get rid of memory errors. When I wrote a go script, I noticed that I received a lot of memory errors. I decided to double check by writing a simple hello world program in Go.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003epackage\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;fmt\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003efmt\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ePrintln\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRunning this then gives me a bunch of memory errors of the form \u003ccode\u003eInvalid read of size 4\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eNow this doesn\u0026rsquo;t mean that the program is leaky. Valgrind does confirm that no leaks are possible. It then begs the question, does this type of memory error even matter?\u003c/p\u003e\n\u003cp\u003eTo avoid thinking about that I decided to play with \u003ca href=\"https://rust-lang.org\"\u003eRust\u003c/a\u003e instead.\u003c/p\u003e\n",
    "date_published": "2019.08.02",
    "tags": null,
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/ohmyzsh/",
    "url": "https://brandonrozek.com/blog/ohmyzsh/",
    "title": "Oh My Zsh",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eZsh is an extension of the sh that contains a lot more features than the default bash shell installed on most systems.\u003c/p\u003e\n\u003cp\u003eFor example, one of my new favorite features is being able to type in part of a command in your history and using the up-arrow to find it.\u003c/p\u003e\n\u003cp\u003eFor example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003evim test.hi\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen typing \u003ccode\u003evim\u003c/code\u003e and up-arrow will expand it to \u003ccode\u003evim test.hi\u003c/code\u003e. Super handy when you have to type of long commands frequently.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eOh My Zsh\u003c/em\u003e is a community project that superpowers zsh which allows you to easily add plugins and themes. The theme I\u0026rsquo;m currently using is \u003ca href=\"https://github.com/romkatv/powerlevel10k#oh-my-zsh\"\u003epowerlevel10k\u003c/a\u003e which is a custom theme.\u003c/p\u003e\n\u003cp\u003eThe list of plugins I have is:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003egit\u003c/li\u003e\n\u003cli\u003ecolored-man-pages\u003c/li\u003e\n\u003cli\u003ecolorize\u003c/li\u003e\n\u003cli\u003ecommand-not-found\u003c/li\u003e\n\u003cli\u003ecp\u003c/li\u003e\n\u003cli\u003ersync\u003c/li\u003e\n\u003cli\u003escreen\u003c/li\u003e\n\u003cli\u003essh-agent\u003c/li\u003e\n\u003cli\u003eweb-search\u003c/li\u003e\n\u003cli\u003edocker\u003c/li\u003e\n\u003cli\u003enode\u003c/li\u003e\n\u003cli\u003enpm\u003c/li\u003e\n\u003cli\u003envm\u003c/li\u003e\n\u003cli\u003egem\u003c/li\u003e\n\u003cli\u003eruby\u003c/li\u003e\n\u003cli\u003epip\u003c/li\u003e\n\u003cli\u003epython\u003c/li\u003e\n\u003cli\u003epyenv\u003c/li\u003e\n\u003cli\u003evirtualenv\u003c/li\u003e\n\u003cli\u003edebian\u003c/li\u003e\n\u003cli\u003esystemd\u003c/li\u003e\n\u003cli\u003esudo\u003c/li\u003e\n\u003cli\u003ezsh-syntax-highlighting\u003c/li\u003e\n\u003c/ul\u003e\n",
    "date_published": "2019.07.21",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/yubikey/",
    "url": "https://brandonrozek.com/blog/yubikey/",
    "title": "YubiKey",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe YubiKey is a hardware authentication device manufactured by Yubico. It is a write only device that is meant to hold various authentication keys. The fact that it is write only means that people can\u0026rsquo;t duplicate these keys. \u003ca href=\"https://twitter.com/kernellinux\"\u003eNoah Chelliah\u003c/a\u003e from Altispeed says that he uses YubiKeys at his company so that when employees moves on from his company he only needs to take their YubiKey.\u003c/p\u003e\n\u003cp\u003eNow I don\u0026rsquo;t manage any infrastructure larger than a couple computers, so why are these things interesting from a home use perspective? Well it allows you to have two-factor authentication separate from your smart phone. I was never a large fan of using my cell phone for two factor since you usually change cell phones every couple years.\u003c/p\u003e\n\u003cp\u003eWhat I wanted to highlight is how easy it is to integrate it with Linux. The Pluggable Authentication Module (PAM) is responsible for authentication and other various utilities in Linux. By adding a couple of lines to a couple of text files, you can add two-factor authentication to your laptop!\u003c/p\u003e\n\u003cp\u003eInstead of rewriting a bunch of instructions I got from somewhere else, I\u0026rsquo;ll direct you to Yubico\u0026rsquo;s own \u003ca href=\"https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html\"\u003edocumentation entry\u003c/a\u003e. Happy hacking!\u003c/p\u003e\n",
    "date_published": "2019.07.07",
    "tags": ["Security"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/rsynckey/",
    "url": "https://brandonrozek.com/blog/rsynckey/",
    "title": "Rsync with a Different Key",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eTo use Rsync with a different key, follow the command structure below.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ersync -e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ssh -i \u003c/span\u003e$HOME\u003cspan style=\"color:#e6db74\"\u003e/.ssh/key\u0026#34;\u003c/span\u003e user@hostname:/from/dir/ /to/dir/\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThough for syncing my local website to my VPS, I usually like having more options with my rsync command\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ersync -Paz --delete -e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ssh -i \u003c/span\u003e$HOME\u003cspan style=\"color:#e6db74\"\u003e/.ssh/key\u0026#34;\u003c/span\u003e user@hostname:/from/dir/ /to/dir/\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eQuick option definitions (from man page)\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eOption\u003c/th\u003e\n          \u003cth\u003eDescription\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e-e\u003c/td\u003e\n          \u003ctd\u003eAllows you to override the default shell used as the transport for rsync.  Command line options are permitted after the command name.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e-a, \u0026ndash;archive\u003c/td\u003e\n          \u003ctd\u003eThis is equivalent to -rlptgoD. It is a quick way of saying you want recursion and want to preserve almost everything (with -H being a notable omission).  The only exception to the above equivalence is when \u0026ndash;files-from is specified, in which case -r is not implied. Note that -a does not preserve hardlinks, because finding multiply-linked files is expensive.  You must separately specify -H.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e-P\u003c/td\u003e\n          \u003ctd\u003eEquivalent to \u0026ndash;partial \u0026ndash;progress.  Its purpose is to make it much easier to specify these two options for a long transfer that may be interrupted.\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e-z, \u0026ndash;compress\u003c/td\u003e\n          \u003ctd\u003eCompress file data during the transfer\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u0026ndash;delete\u003c/td\u003e\n          \u003ctd\u003eDelete extraneous files from dest dirs\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n",
    "date_published": "2019.07.06",
    "tags": null,
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/simulators/",
    "url": "https://brandonrozek.com/blog/simulators/",
    "title": "Simulators",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003ePart of my job involves integrating multiple different sensors together to make a cohesive product. One thing that I appreciate about my current team, is the rich set of tooling built around the project. I am definitely learning a lot from this project. One thing I wanted to share was the use of \u003cem\u003esimulators\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eNow since we\u0026rsquo;re working with sensor data, it doesn\u0026rsquo;t always make sense to need to be connected to a whole system. There\u0026rsquo;s also the additional hassle of dealing with safety and working outside. So in order to do some programming and still be at ease that we didn\u0026rsquo;t break anything, we make heavy use of simulators.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eExample\u003c/strong\u003e: Have a GPS hooked up to your system? Well with a tool you can make waypoints and generate a file which you then feed in as a fake GPS device!\u003c/p\u003e\n\u003cp\u003eNow when I say fake device, I don\u0026rsquo;t mean I\u0026rsquo;m actually emulating the device on the machine. It\u0026rsquo;s easier to have your device interfacers publish to the local network and have your other parts of the application subscribe to it. So in this case, you will just need to produce those messages directly.\u003c/p\u003e\n\u003cp\u003eSo in conclusion, I think this pattern will work well for my personal projects. It helps keep your code modular and easily testable!\u003c/p\u003e\n",
    "date_published": "2019.07.01",
    "tags": null,
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/discoveringhamradio/",
    "url": "https://brandonrozek.com/blog/discoveringhamradio/",
    "title": "Discovering Ham Radio",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI feel like Ham Radio has been hiding in the background as I move through life. I knew someone that kept a Ham radio in his car, Noah talks about it frequently on his \u003ca href=\"https://asknoahshow.com/\"\u003epodcast\u003c/a\u003e, and it seemed like the next step after \u003ca href=\"https://github.com/brandon-rozek/radiotuner\"\u003eplaying with\u003c/a\u003e  \u003ca href=\"https://www.amazon.com/RTL-SDR-Blog-RTL2832U-Software-Telescopic/dp/B011HVUEME/ref=sr_1_3\"\u003esoftware defined radio\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eOne of the things that I think pushed me over to wanting to get my Technician\u0026rsquo;s license is the discovery of \u003ca href=\"http://www.arrl.org/digital-data-modes\"\u003edigital data modes\u003c/a\u003e. Hams even have their own \u003ca href=\"https://www.winlink.org/\"\u003eemail\u003c/a\u003e!\u003c/p\u003e\n\u003cp\u003eWhy is this more interesting than Internet? Well Ham Radio doesn\u0026rsquo;t need any physical infrastructure setup (other than your radio and maybe some repeaters) to get you connected to other people. In fact, Hams often \u003ca href=\"http://www.arrl.org/public-service\"\u003evolunteer\u003c/a\u003e in natural disasters when the phone systems are down.\u003c/p\u003e\n\u003cp\u003eThis convinced to buy a \u003ca href=\"https://www.amazon.com/ARRL-Radio-License-Manual-Spiral/dp/1625950829/ref=sr_1_6\"\u003estudy guide\u003c/a\u003e and start preparing to take my technicians test this summer. If you\u0026rsquo;re interested in Ham Radio, please contact me at my email on my home page :)\u003c/p\u003e\n",
    "date_published": "2019.07.01",
    "tags": ["Amateur Radio"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/gitlabcicd/",
    "url": "https://brandonrozek.com/blog/gitlabcicd/",
    "title": "Gitlab CI/CD",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOne of the greatest benefits I believe of integrating Gitlab\u0026rsquo;s CI/CD into your workload is code linting and building. This immediately eliminates the easy mistakes that developers make and wastes less people\u0026rsquo;s time.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;m working towards getting the build setup just right in one of the projects I\u0026rsquo;m currently working. However, in the meantime, Gitlab supports running it\u0026rsquo;s CI/CD jobs under the \u003ccode\u003eshell executor\u003c/code\u003e. I would like to go to the default Docker route in the future once I figure out all of the dependencies of the project I\u0026rsquo;m building.\u003c/p\u003e\n",
    "date_published": "2019.07.01",
    "tags": null,
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/bashprocesses/",
    "url": "https://brandonrozek.com/blog/bashprocesses/",
    "title": "Handling Background Processes in Bash",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eFor multi-process applications, I want to be able to start it up using the \u003ccode\u003ebash\u003c/code\u003e command processor and be able to stop all the processes just by hitting \u003ccode\u003eCTRL-C\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eAs a quick reminder, to have a task run in the background you need to add a \u003ccode\u003e\u0026amp;\u003c/code\u003e at the end of the line.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexecute_app \u0026amp;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003ePreviously, I was grabbing the PID of this background process, trapping the interrupt signal and taking the time to send the interrupt signal to all of the background processes.\u003c/p\u003e\n\u003cp\u003eYou can get the child pid by referencing the variable \u003ccode\u003e$!\u003c/code\u003e after sending a process to the background.\u003c/p\u003e\n\u003cp\u003eNow I just use \u003ccode\u003esetsid\u003c/code\u003e to set the process group of the background processes to be the same as the bash process itself.  The following demo script here will show the capability.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003esetsid sleep \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e \u0026amp;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esetsid sleep \u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e \u0026amp;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewait\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis script will send two processes to the background and will wait until all the processes are finished. Hitting \u003ccode\u003eCTRL-C\u003c/code\u003e during execution will send the interrupt signal to all of the processes achieving my goal.\u003c/p\u003e\n",
    "date_published": "2019.06.17",
    "tags": ["Bash"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/whyzeromq/",
    "url": "https://brandonrozek.com/blog/whyzeromq/",
    "title": "Why ZeroMQ",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve been playing around with ZeroMQ recently and it\u0026rsquo;s been really exciting. This blog post is going to outline why I think you should be using ZeroMQ today.\u003c/p\u003e\n\u003cp\u003eFirst of all, before you compare this to other products like RabbitMQ, DDS, etc. Realize that this is a static library that you link to in your application as opposed to a broker you run. This means that you can get the benefits with this library for very little overhead.\u003c/p\u003e\n\u003ch2 id=\"easier-sockets\"\u003eEasier Sockets\u003c/h2\u003e\n\u003cp\u003eInstead of talking about how easy ZeroMQ is to use, I\u0026rsquo;m going to just show you the code to implement a server-client relationship in Python.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eServer.py\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e zmq\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e time\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eport \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;5556\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econtext \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e zmq\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eContext()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esocket \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e context\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esocket(zmq\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eREP)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esocket\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebind(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;tcp://*:\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e%s\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e%\u003c/span\u003e port)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eTrue\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    msg \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e socket\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003erecv()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(msg)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    time\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esleep(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    socket\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esend_string(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Server Message To Client\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eClient.py\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e zmq\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e time\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eport \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;5556\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003econtext \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e zmq\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eContext()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esocket \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e context\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esocket(zmq\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eREQ)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esocket\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003econnect(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;tcp://localhost:\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e%s\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e%\u003c/span\u003e port)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eTrue\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    socket\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esend_string(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;client message to server\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    time\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esleep(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    msg \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e socket\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003erecv()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    print(msg)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd just like that we have a way to transport messages back and forth. No need to make a special header before each message to know the appropriate size of the packets. ZeroMQ abstracts those details from you.\u003c/p\u003e\n\u003ch2 id=\"different-transports-available\"\u003eDifferent Transports Available\u003c/h2\u003e\n\u003cp\u003eYou\u0026rsquo;re not limited to only \u003ccode\u003eTCP\u003c/code\u003e. You can use \u003ccode\u003einproc\u003c/code\u003e for thread-to-thread messaging, \u003ccode\u003eipc\u003c/code\u003e for inter-process communication, and \u003ccode\u003eepgm\u003c/code\u003e or \u003ccode\u003epgm\u003c/code\u003e for multicast messaging. Most of the time, just changing the connection string in \u003ccode\u003esocket.connect\u003c/code\u003e just works!\u003c/p\u003e\n\u003ch2 id=\"common-networking-patterns-built-in\"\u003eCommon Networking Patterns Built In\u003c/h2\u003e\n\u003cp\u003eSometimes we just want a dumb pipe between two ends (\u003ccode\u003epipe\u003c/code\u003e) but most of the times we\u0026rsquo;re writing applications that follow the \u003ccode\u003eserver-client\u003c/code\u003e or \u003ccode\u003epublisher-subscriber\u003c/code\u003e architecture. That gets defined in \u003ccode\u003econtext.socket\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eEx: \u003ccode\u003econtext.socket(zmq.PAIR)\u003c/code\u003e\u003c/p\u003e\n",
    "date_published": "2019.06.16",
    "tags": ["Networking"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/debuggingandperformance/",
    "url": "https://brandonrozek.com/blog/debuggingandperformance/",
    "title": "Debugging and Performance",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve come to like the GNU Debugger (GDB) and the \u003ccode\u003eperf\u003c/code\u003e tool recently. This post will be a short summary of the various interesting commands you can use.\u003c/p\u003e\n\u003ch2 id=\"gnu-debugger\"\u003eGNU Debugger\u003c/h2\u003e\n\u003cp\u003eTo attach gbd to an existing process do the following\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egdb -p pid_of_process\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOtherwise you can start a new application\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egdb name_of_executable\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOnce it loads it will bring you into it\u0026rsquo;s own \u003ccode\u003eREPL\u003c/code\u003e environment. Usually once this comes up I find it useful to add breakpoints to the program. You can either do it by function name or by line number.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003egdb\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e break FunctionName\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003egdb\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e break code.cpp:81\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you just started a new application you can begin running the program with whatever arguments you wish\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003egdb\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e run -arg1 -arg2\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you have attached to a process then you can continue its execution.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003egdb\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003econtinue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"breakpoints\"\u003eBreakpoints\u003c/h3\u003e\n\u003cp\u003eIf you have set a breakpoint, it will stop the processes\u0026rsquo; execution when it lands on the breakpoint. From here, we can take a look at what\u0026rsquo;s on the stack, print variables, and do whatever other debugging we wish.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePrint variables on stack:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003egdb\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e info locals\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003ePrint a specific variable:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003egdb\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e print variable_name\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eShow backtrace:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003egdb\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e bt\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eContinue on with program execution:\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003egdb\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003econtinue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"perf\"\u003ePerf\u003c/h2\u003e\n\u003cp\u003eI haven\u0026rsquo;t played with \u003ccode\u003eperf\u003c/code\u003e as much but one thing I found that was cool was the \u003ccode\u003eperf top\u003c/code\u003e command. This command gives you samples of which function calls keeps the program the most busy.\u003c/p\u003e\n\u003cp\u003eTo attach to a process and show samples:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eperf top -p pid -K\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.06.15",
    "tags": ["Testing"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/linuxdesktopicons/",
    "url": "https://brandonrozek.com/blog/linuxdesktopicons/",
    "title": "Linux Desktop Icons",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI get asked a decent number of times how to add desktop icons on Linux. Luckily it\u0026rsquo;s incredibly easy. \u003ca href=\"http://standards.freedesktop.org/desktop-entry-spec/latest/\"\u003eIt\u0026rsquo;s a \u003ccode\u003efreedesktop\u003c/code\u003e standard\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eIn fact the simplest file would follow the format:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003eDesktop Entry]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eName=Application Name\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eExec=/path/to/executable -randomFlag\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eIcon=/optional/path/to/icon\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eTerminal=false\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003eType=Application\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOnce you have this saved to \u003ccode\u003eyourapplication.desktop\u003c/code\u003e move it to either \u003ccode\u003e/usr/share/applications\u003c/code\u003e if you want it system-wide or \u003ccode\u003e/home/user/.local/share/applications\u003c/code\u003e if you want it just for your \u003ccode\u003euser\u003c/code\u003e.\u003c/p\u003e\n",
    "date_published": "2019.06.03",
    "tags": ["Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/custom-system-fonts/",
    "url": "https://brandonrozek.com/blog/custom-system-fonts/",
    "title": "Custom System Fonts",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cstrong\u003eWarning: This blog post partially applies to Ubuntu-based operating systems\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eI wanted to add a font to an image on GIMP. Now I wanted to make it so that the font is available system-wide in case I ever needed it again.\u003c/p\u003e\n\u003cp\u003eFollowing this \u003ca href=\"https://itsfoss.com/install-fonts-ubuntu/\"\u003eblog post from It\u0026rsquo;s FOSS\u003c/a\u003e I decided to make a \u003ccode\u003e.fonts\u003c/code\u003e folder in my home directory. After that you can download various font\u0026rsquo;s from \u003ca href=\"https://www.fontsquirrel.com/\"\u003eFont Squirrel\u003c/a\u003e, \u003ca href=\"https://www.dafont.com\"\u003edafont\u003c/a\u003e, \u003ca href=\"https://fonts.google.com/\"\u003eGoogle Fonts\u003c/a\u003e, and others and drop the \u003ccode\u003e.otf\u003c/code\u003e or \u003ccode\u003e.ttf\u003c/code\u003e files into the \u003ccode\u003e.fonts\u003c/code\u003e folder.\u003c/p\u003e\n\u003cp\u003eThen to finally achieve my goal, I made sure I added \u003ccode\u003e/home/user/.fonts\u003c/code\u003e folder to my fonts as outlined on the \u003ca href=\"https://docs.gimp.org/2.10/en/gimp-using-fonts.html\"\u003eGIMP documentation\u003c/a\u003e\u003c/p\u003e\n",
    "date_published": "2019.05.27",
    "tags": ["Ubuntu","Linux"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/unattended-upgrades/",
    "url": "https://brandonrozek.com/blog/unattended-upgrades/",
    "title": "Unattended Upgrades",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;m a big believer of reducing maintenance. One of the things that I didn\u0026rsquo;t bother setting up before but should\u0026rsquo;ve is \u003cem\u003eunattended upgrades\u003c/em\u003e. The benefits of this is that you don\u0026rsquo;t have to repeatedly log into your sever to upgrade your system.\u003c/p\u003e\n\u003cp\u003eWhy bother managing your own server in the first place? Well with a virtualized system you can set up any type of application that you like. You\u0026rsquo;re not limited to only hosting static sites or whatever applications the C-Panel instance allows.\u003c/p\u003e\n\u003cp\u003eNow there\u0026rsquo;s no need for me to reiterate what\u0026rsquo;s been done before, so here\u0026rsquo;s a \u003ca href=\"https://libre-software.net/ubuntu-automatic-updates/\"\u003euseful post from libre-software\u003c/a\u003e describing how to set it up so that a Ubuntu server updates itself automatically.\u003c/p\u003e\n",
    "date_published": "2019.05.26",
    "tags": ["Linux","Ubuntu"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/vncsetup/",
    "url": "https://brandonrozek.com/blog/vncsetup/",
    "title": "VNC Setup",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI mostly following this \u003ca href=\"https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-vnc-on-ubuntu-18-04\"\u003eDigital Ocean Tutorial\u003c/a\u003e on getting a working VNC setup on my desktop computer. There is one main difference on my end since I use KDE. I edited the \u003ccode\u003e/home/$USER/.vnc/xstartup\u003c/code\u003e file to appear as\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/sh\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003eunset SESSION_MANAGER\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eunset DBUS_SESSION_BUS_ADDRESS\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edbus-launch --exit-with-session startkde\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd I added the following script to make my life easier\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#!/bin/bash\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003essh -L 5901:127.0.0.1:5901 -C -N -l rozek 192.168.0.104 \u0026amp;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ekrdc vnc://127.0.0.1:5901\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.05.24",
    "tags": null,
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/usbredirection/",
    "url": "https://brandonrozek.com/blog/usbredirection/",
    "title": "Wacom and USB Redirection in Virtual Machines",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003ca href=\"https://virt-manager.org/\"\u003eVirt-Manager\u003c/a\u003e is a great tool for managing virtual machines under Linux. Today I learned of \u003ca href=\"https://blog.wikichoon.com/2014/04/spice-usb-redirection-in-virt-manager.html\"\u003eSpice USB redirection\u003c/a\u003e. Essentially it allows you to switch USB devices from the host to the virtualized environment. This came in handy when I noticed that the graphics tablet device was not able to do pressure sensitivity on the Windows guest.\u003c/p\u003e\n\u003cp\u003eTo achieve this goal, I removed the graphics tablet hardware device and manually redirected that USB device to the guest. I then remembered to install the \u003ca href=\"https://www.wacom.com/en-us/support/product-support/drivers\"\u003eWacom drivers\u003c/a\u003e for the Windows VM.\u003c/p\u003e\n",
    "date_published": "2019.05.24",
    "tags": ["Linux","Virtualization"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/externalmediaformats/",
    "url": "https://brandonrozek.com/blog/externalmediaformats/",
    "title": "External Media Formats",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI received an external SSD recently and I decided that it would be a great place to offload some of my backups. Before I got started, I became curious as to what filesystem to put on the SSD. After some research, it seems that if I want to be able to access it using Windows I am actually quite limited. In fact only three make sense:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eFAT32\u003c/li\u003e\n\u003cli\u003eexFAT\u003c/li\u003e\n\u003cli\u003eNTFS\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eNow FAT32 is a legacy filesystem, Windows 95 type of legacy. So let\u0026rsquo;s rule that one out. exFAT is supposed to be based on FAT32 but increases the file size limits significantly. It is also known for being very slim and as such does not having journaling support. NTFS is the filesystem that Windows 10 uses for default for it\u0026rsquo;s installation. That file system supports journaling.\u003c/p\u003e\n\u003cp\u003eI ended up going with NTFS due to its journaling support. Why is journaling important to me? Well this helps keep the file system internally consistent. The details are outside the scope of this post, but \u003ca href=\"https://en.wikipedia.org/wiki/Journaling_file_system\"\u003eWikipedia has a nice entry\u003c/a\u003e about it.\u003c/p\u003e\n\u003cp\u003eWell as it turns out, the drive that I was going to use already was formatted as NTFS. Though for sake of completeness, I will say that you can format drives with tools like \u003ccode\u003eparted\u003c/code\u003e, \u003ccode\u003egparted\u003c/code\u003e, and \u003ccode\u003epartitionmanager\u003c/code\u003e. Just make sure that the drive you\u0026rsquo;re formatting is what you think it is!\u003c/p\u003e\n",
    "date_published": "2019.05.22",
    "tags": ["Storage"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/ctf/",
    "url": "https://brandonrozek.com/blog/ctf/",
    "title": "Capture The Flag",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThere is an event in Computer Security called \u0026ldquo;Capture The Flag\u0026rdquo;. The purpose is to test the skills of security engineers and students through a variety of tasks in order to get a pass phrase which is called a flag. I noticed that Dr. Andrew Marshall wanted to set up this event at the University of Mary Washington for a while, so I and a few others decided to help make this a reality for him.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eI didn\u0026rsquo;t know you studied security?\u003c/strong\u003e Well honestly I don\u0026rsquo;t. But that doesn\u0026rsquo;t stop me from learning and creating cool challenges.\u003c/p\u003e\n\u003ch2 id=\"challenges-i-implemented\"\u003eChallenges I Implemented\u003c/h2\u003e\n\u003cp\u003eIn this post, I\u0026rsquo;ll describe the various challenges I\u0026rsquo;ve implemented. I won\u0026rsquo;t give the full problem description or answer just in case the problems get recycled in later years.\u003c/p\u003e\n\u003ch3 id=\"web-archive-search\"\u003eWeb Archive Search\u003c/h3\u003e\n\u003cp\u003eIn this problem I describe a discussion that an alumni and the student has. The alumni has trouble remembering a certain detail of an event held on campus. Key words and descriptions are given about the event and it is the job of the student to find the detail that the alumni wants.\u003c/p\u003e\n\u003cp\u003eThe juicy part of this problem is that the event is no longer on the current version of the university website. This means that ideally the student would go to the \u003ca href=\"http://web.archive.org/\"\u003eWeb Archive\u003c/a\u003e to search and find for the information.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eFunny enough, one contestant found the information by reading a really old publication from the time period described.\u003c/em\u003e\u003c/p\u003e\n\u003ch3 id=\"audio-recording\"\u003eAudio Recording\u003c/h3\u003e\n\u003cp\u003eIn this challenge, I recorded a flag in an audio recording and distorted it using various tools in audacity. The idea is that you would get the audio clip and not be able to hear what the flag is and then have to import it into an audio editor of choice and manipulate it until the flag is clear.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eSadly no one was able to complete this challenge.\u003c/em\u003e I think I made it too hard by voicing individual characters instead of doing a phrase. This would make ambiguous letters like \u0026ldquo;B\u0026rdquo; and \u0026ldquo;P\u0026rdquo; barely distinguishable from each other.\u003c/p\u003e\n\u003ch3 id=\"hidden-text\"\u003eHidden Text\u003c/h3\u003e\n\u003cp\u003eThis challenge involves hiding text in the web page through some means. Hidden Text was meant to be one of the easiest problems to ease people onto the competition. You can easily solve this type of challenge by opening up the web inspector tool.\u003c/p\u003e\n",
    "date_published": "2019.05.22",
    "tags": ["Security"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/pythonsymmetricgroups/",
    "url": "https://brandonrozek.com/blog/pythonsymmetricgroups/",
    "title": "Symmetric Groups in Python",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cstrong\u003eWarning:\u003c/strong\u003e This post is meant for someone whose familiar with concepts of Abstract Algebra.\u003c/p\u003e\n\u003ch2 id=\"refresher\"\u003eRefresher\u003c/h2\u003e\n\u003ch3 id=\"definitions\"\u003eDefinitions\u003c/h3\u003e\n\u003cp\u003eAn \u003cstrong\u003eoperation\u003c/strong\u003e on a set is a calculation that maps one element in a set onto another element of the set.\u003c/p\u003e\n\u003cp\u003eA \u003cstrong\u003egroup\u003c/strong\u003e in mathematics is a set and an operation that follows the three properties:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThere exists an identity element.\u003c/li\u003e\n\u003cli\u003eThe operation is associative.\u003c/li\u003e\n\u003cli\u003eFor every element, there exists an inverse of that element in the set.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eSymmetric Groups\u003c/strong\u003e are groups whose elements are all bijections from the set onto itself and operation which is composition of functions.\u003c/p\u003e\n\u003ch3 id=\"example\"\u003eExample\u003c/h3\u003e\n\u003cp\u003eLet\u0026rsquo;s look at the group $\\mathbb{Z}_3$. Here is an example of an element of its symmetric group.\n$$\n\\begin{pmatrix}\n0 \u0026amp; 1 \u0026amp; 2 \\\n1 \u0026amp; 2 \u0026amp; 0\n\\end{pmatrix}\n$$\nThis element maps $0 \\rightarrow 1$, $1 \\rightarrow 2$, and $2 \\rightarrow 0$.\u003c/p\u003e\n\u003cp\u003eA good way to check if something similar to the above is an element of a symmetric group is pay attention to the second row. Make sure that it only contains the elements of the set you care about (ex: $\\mathbb{Z}_3$) and that there are no repeats.\u003c/p\u003e\n\u003cp\u003eLet\u0026rsquo;s look at an example of composing two elements from this symmetric group.\u003c/p\u003e\n\u003cp\u003e$$\n\\begin{pmatrix}\n0 \u0026amp; 1 \u0026amp; 2 \\\n1 \u0026amp; 2 \u0026amp; 0\n\\end{pmatrix}\n\\circ\n\\begin{pmatrix}\n0 \u0026amp; 1 \u0026amp; 2 \\\n0 \u0026amp; 2 \u0026amp; 1 \\\n\\end{pmatrix}\n=\n\\begin{pmatrix}\n0 \u0026amp; 1 \u0026amp; 2 \\\n1 \u0026amp; 0 \u0026amp; 2 \\\n\\end{pmatrix}\n$$\u003c/p\u003e\n\u003cp\u003eThe main thing to remember here is that you must compose from right to left.\u003c/p\u003e\n\u003cp\u003e$0 \\rightarrow 0$ and then $0 \\rightarrow 1$, so ultimately $0 \\rightarrow 1$.\u003c/p\u003e\n\u003cp\u003e$1 \\rightarrow 2$ and $2 \\rightarrow 0$, so ultimately $1 \\rightarrow 0$.\u003c/p\u003e\n\u003cp\u003e$2 \\rightarrow 1$ and $1 \\rightarrow 2$, so ultimately $2 \\rightarrow 2$.\u003c/p\u003e\n\u003ch3 id=\"finding-inverses\"\u003eFinding Inverses\u003c/h3\u003e\n\u003cp\u003eFinding the inverse is simple, since all you need to do is flip the two rows and sort it again.\n$$\n\\begin{pmatrix}\n0 \u0026amp; 1 \u0026amp; 2 \\\n1 \u0026amp; 2 \u0026amp; 0\n\\end{pmatrix}^{-1} =\n\\begin{pmatrix}\n1 \u0026amp; 2 \u0026amp; 0 \\\n0 \u0026amp; 1 \u0026amp; 2\n\\end{pmatrix} =\n\\begin{pmatrix}\n0 \u0026amp; 1 \u0026amp; 2 \\\n2 \u0026amp; 0 \u0026amp; 1\n\\end{pmatrix}\n$$\u003c/p\u003e\n\u003ch3 id=\"code-implementation\"\u003eCode Implementation\u003c/h3\u003e\n\u003cp\u003eFor Abstract Algebra homework, there was a lot of compositions of these symmetric elements. Sadly, I get pretty lazy doing these by hand for many hours. So like any Computer Scientist, I created a simple script in Python to help me compute these.\u003c/p\u003e\n\u003cp\u003eThe code is located in \u003ca href=\"https://gist.github.com/Brandon-Rozek/adf9e1e64e2fbfcd3f8d3bc5da9322bf\"\u003ethis gist\u003c/a\u003e.\u003c/p\u003e\n\u003ch4 id=\"basic-usage\"\u003eBasic Usage\u003c/h4\u003e\n\u003cp\u003e\u003ccode\u003eSymmetricElement\u003c/code\u003e takes in the second row of the matrices we were playing with. You can find the inverse with \u003ccode\u003eelement.inverse()\u003c/code\u003e and you can compose two symmetric elements together with the \u003ccode\u003e*\u003c/code\u003e operation.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSymmetricElement(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# array([[1., 2., 3.],\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#       [1., 2., 3.]])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSymmetricElement(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e SymmetricElement(\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#array([[1., 2., 3.],\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#       [2., 1., 3.]])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSymmetricElement(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e)\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003einverse()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#array([[1., 2., 3.],\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#       [1., 2., 3.]])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.05.22",
    "tags": ["Math","Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/colormanipulation/",
    "url": "https://brandonrozek.com/blog/colormanipulation/",
    "title": "Color Manipulation with Sass",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThere are many times that I need to slightly mess with a color. The easiest way I found to do it is to use one of the many color functions in the program \u003ccode\u003eSass\u003c/code\u003e. \u003ca href=\"https://sass-lang.com/\"\u003eSass\u003c/a\u003e is a CSS preprocessor, meaning that it has it\u0026rsquo;s own syntax and it compiles down to CSS. I remember using this before CSS variables became a thing and that was one of the main driving points of Sass.\u003c/p\u003e\n\u003cp\u003eSince I don\u0026rsquo;t work in Web Development anymore, I don\u0026rsquo;t actually have Sass installed on my computer but instead go to \u003ca href=\"https://www.sassmeister.com/\"\u003eSassMeister.com\u003c/a\u003e to do my color manipulations. ThoughtBot already wrote a really nice \u003ca href=\"https://thoughtbot.com/blog/controlling-color-with-sass-color-functions/\"\u003epost\u003c/a\u003e describing all the different color functions, but I\u0026rsquo;ll quickly describe what I do to mess with colors.\u003c/p\u003e\n\u003ch2 id=\"quick-color-manipulation\"\u003eQuick Color Manipulation\u003c/h2\u003e\n\u003cp\u003eNow you do need to write special scss syntax in order for this to work, but luckily most of it you can just copy paste and change what you want. For example, to get Sass to make red 10% lighter you would do the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scss\" data-lang=\"scss\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003ep\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003ecolor\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003elighten\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003ered\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSass will then output the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003ep\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ecolor\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e#ff3333\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd BAM just like that you got the hex code for a lighter version of red. You can also get all crazy and nest statements into each other which is usually what I do when I play with these colors.\u003c/p\u003e\n\u003cp\u003eIf you rather have the RGBA version of this you can do the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-scss\" data-lang=\"scss\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003ep\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003ecolor\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ergba\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003elighten\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003ered\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e)\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e.5\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd it will output:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003ep\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ecolor\u003c/span\u003e: rgba(\u003cspan style=\"color:#ae81ff\"\u003e255\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e51\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e51\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0.5\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSo in summary, all you need is to replace the section after \u003ccode\u003ecolor:\u003c/code\u003e and you\u0026rsquo;re all set for manipulating colors! Check out the \u003ca href=\"https://thoughtbot.com/blog/controlling-color-with-sass-color-functions\"\u003eThoughtBot Post above\u003c/a\u003e in order to see all the variety of functions included with Sass such as saturating, hue-shifting, and much more.\u003c/p\u003e\n",
    "date_published": "2019.05.21",
    "tags": ["Web","CSS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/virtualenv/",
    "url": "https://brandonrozek.com/blog/virtualenv/",
    "title": "Python Virtual Environments",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eDependency management is hard.  Luckily with Python there is a program called \u003ccode\u003evirtualenv\u003c/code\u003e that can help isolate different projects and manage dependencies.\u003c/p\u003e\n\u003ch2 id=\"commands\"\u003eCommands\u003c/h2\u003e\n\u003cp\u003eTo create a new python 3.7 environment type in the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003evirtualenv --python\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003epython3.7 environment_name\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOf course you can replace the python version with whichever version you like. Now to go into the environment do the following:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esource environment_name/bin/activate\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis now sets up your python interpreter and other utilities to use the installation in the \u003ccode\u003eenvironment_name\u003c/code\u003e folder. You can now install python packages using \u003ccode\u003epip\u003c/code\u003e and have it only reside in this environment.\u003c/p\u003e\n\u003cp\u003eTo save all currently installed packages into \u003ccode\u003erequirements.txt\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip freeze \u0026gt; requirements.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can then install those packages in a different virtualenv session with:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epip install -r requirements.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can leave the virtualenv session with:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edeactivate\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.05.21",
    "tags": ["Python"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/borgbackup/",
    "url": "https://brandonrozek.com/blog/borgbackup/",
    "title": "Borg Backup",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cstrong\u003eNote:\u003c/strong\u003e I switched to using \u003ca href=\"https://restic.net/\"\u003eRestic\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eI started using \u003ca href=\"https://www.borgbackup.org/\"\u003eBorg Backup\u003c/a\u003e in order to efficiently and securely do my backups. I did some research before choosing this solution as I required three things:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eCompression\u003c/li\u003e\n\u003cli\u003eEncryption\u003c/li\u003e\n\u003cli\u003eDeduplication\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eEach point is important on their own. Ideally I would be able to put this onto a cloud solution. Due to this I would want compression and deduplication to keep my costs down and encryption in order to maintain privacy.\u003c/p\u003e\n\u003cp\u003eLuckily Borg does all of these things and more! It\u0026rsquo;s also easily available on the AUR and the Ubuntu repositories.\u003c/p\u003e\n\u003ch2 id=\"getting-started\"\u003eGetting Started\u003c/h2\u003e\n\u003cp\u003eThis will be a short post just describing the basic usage of the tool. I haven\u0026rsquo;t fully implemented this tool yet so forgive me if this doesn\u0026rsquo;t match your exact use case. This is also a great way for me to document the basic commands for myself as well.\u003c/p\u003e\n\u003cp\u003eFirst to initialize a borg repo encrypted with a password at \u003ccode\u003erepolocation\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eborg init --encryption\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003erepokey repolocation\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen to create a backup in the repo with key \u003ccode\u003ebackup1\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eborg create --stats --progress --compression lzma repolocation::backup1 folderToBackup\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can actually replace the compression algorithm if wanted, here is a short description from their \u003ca href=\"https://borgbackup.readthedocs.io/en/stable/\"\u003ewebsite\u003c/a\u003e:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003elz4 (super fast, low compression)\u003c/li\u003e\n\u003cli\u003ezstd (wide range from high speed and low compression to high compression and lower speed)\u003c/li\u003e\n\u003cli\u003ezlib (medium speed and compression)\u003c/li\u003e\n\u003cli\u003elzma (low speed, high compression)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eTo list what backups you have in the repo:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eborg list repolocation\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo mount and unmount the repository\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eborg mount repolocation mountlocation\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eborg umount mountlocation\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.05.21",
    "tags": ["Backup"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/screwuppresentations/",
    "url": "https://brandonrozek.com/blog/screwuppresentations/",
    "title": "Notes on '13 Ways Designers Screw Up Client Presentations'",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI watched a great talk by \u003ca href=\"http://mikemonteiro.com/\"\u003eMike Monteiro\u003c/a\u003e recently called \u003ca href=\"https://www.youtube.com/watch?v=IXXKqwrEql4\"\u003e13 Ways Designers Screw Up Client Presentations\u003c/a\u003e. You should really watch the talk but this post is just a quick summary of the points he got across in the presentation.\u003c/p\u003e\n\u003ch2 id=\"intro\"\u003eIntro\u003c/h2\u003e\n\u003cp\u003eYou need to sell your work! Great work is worth nothing if you can\u0026rsquo;t persuade the client it is good.\u003c/p\u003e\n\u003cp\u003eDon\u0026rsquo;t leave the selling to other people. That is your job. Every decision you make is backed by some data or experience. If you\u0026rsquo;re not there, then you lose context and won\u0026rsquo;t be there for feedback.\u003c/p\u003e\n\u003cp\u003eOn to the mistakes\u0026hellip;.\u003c/p\u003e\n\u003ch2 id=\"1-you-are-not-there-to-be-the-clients-friend\"\u003e1. You are not there to be the client\u0026rsquo;s friend\u003c/h2\u003e\n\u003cp\u003eYou are there to solve the client\u0026rsquo;s goal. Happiness is the side-effect of good design, not a goal.\u003c/p\u003e\n\u003cp\u003eYou are there to convince the client not to go down avenues that run counter to their goals. That\u0026rsquo;s what they hired you for. You need to present yourself as the expert that they believed they hired.\u003c/p\u003e\n\u003cp\u003eFor example, your doctor needs to tell you if you have a cracked tooth. Do you think that they would hide this information from you because it might hurt your feelings? No, because it is their job to tell you this information.\u003c/p\u003e\n\u003ch2 id=\"2-not-getting-off-your-ass\"\u003e2. Not getting off your ass\u003c/h2\u003e\n\u003cp\u003eIt should be obvious when you enter the room, who is in charge of the presentation.\u003c/p\u003e\n\u003cp\u003eConfidence isn\u0026rsquo;t about you feeling better, it is about your client feeling better.\u003c/p\u003e\n\u003ch2 id=\"3-starting-with-an-apology\"\u003e3. Starting with an apology\u003c/h2\u003e\n\u003cp\u003eEvery time you apologize for something you are freaking the client out. No matter how much you hoped to present at the presentation, by the time you get to that room, you have the perfect amount of stuff.\u003c/p\u003e\n\u003cp\u003eIf you don\u0026rsquo;t feel like the work is up to spec, cancel the presentation. Do not have a presentation of work you cannot stand behind. It is better to cancel than waste people\u0026rsquo;s time. You can get away with this exactly once during a project.\u003c/p\u003e\n\u003cp\u003eYour client probably works for somebody higher up, the minute you start apologizing to them, they imagine themselves apologizing to their boss!\u003c/p\u003e\n\u003ch2 id=\"4-not-setting-the-stage-properly\"\u003e4. Not Setting the Stage Properly\u003c/h2\u003e\n\u003cp\u003eYou have gathered busy people together, they probably have other things they can be doing, so let them know what this presentation is for.\u003c/p\u003e\n\u003cp\u003eLet them know why they are a necessary and important part of this conversation.\u003c/p\u003e\n\u003cp\u003eYou need to address these two things at the beginning of the presentation\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eWhy are we here?\n\u003col\u003e\n\u003cli\u003eLet them know what their roles are, why they are here, what you are going to show, and what you need from them.\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003cli\u003eWhen can we leave?\n\u003col\u003e\n\u003cli\u003eLet them know what it takes for the goal of the meeting to be met. The minute you get it, the meeting is over. Never give a client time to unmake a decision, especially a decision in your favor.\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eOnce you have gotten what you need, keep quiet. Everything you do beyond that threatens to undo the victory you just got.\u003c/p\u003e\n\u003ch2 id=\"5-giving-the-real-estate-tour\"\u003e5. Giving the Real Estate Tour\u003c/h2\u003e\n\u003cp\u003eYou don\u0026rsquo;t sell a house by talking about the sheetrock. You sell it by getting a buyer to picture themselves in the neighborhood, by picturing the kids in the playground, etc.\u003c/p\u003e\n\u003cp\u003eYou sell the benefits of the work, and you sell how the work matches the project goals.\u003c/p\u003e\n\u003cp\u003eEvery decision on that page should be made with the benefit of data and good research, but people make decisions based on stories.\u003c/p\u003e\n\u003ch2 id=\"6-taking-notes\"\u003e6. Taking Notes\u003c/h2\u003e\n\u003cp\u003eFind somebody else to take the notes. You are giving the presentation.\u003c/p\u003e\n\u003ch2 id=\"7-reading-a-script\"\u003e7. Reading a Script\u003c/h2\u003e\n\u003cp\u003eYou need to convince the client that you are excited about the product.\u003c/p\u003e\n\u003cp\u003e\u0026ldquo;Without promotion something terrible happens\u0026hellip;. Nothing\u0026rdquo; - PT. Barnum\u003c/p\u003e\n\u003ch2 id=\"8-getting-defensive\"\u003e8. Getting Defensive\u003c/h2\u003e\n\u003cp\u003eYou are not your work, and your work is not you.\u003c/p\u003e\n\u003cp\u003eYour work is a product made to meet a client\u0026rsquo;s goals. The client is free to criticize the work and is free to tell you whether they believe it has met the goals or not. You are free to present evidence to the contrary but not get hurt by the criticism.\u003c/p\u003e\n\u003cp\u003eWhen the client starts critiquing the work listen to what they\u0026rsquo;re saying, don\u0026rsquo;t feel like you have to defend everything they\u0026rsquo;re saying right then and there. You also don\u0026rsquo;t have to promise them anything then and there, sometimes it\u0026rsquo;s best to sit on it for a while.\u003c/p\u003e\n\u003cp\u003eWhen a client is giving you feedback it is a great time to keep your mouth shut.\u003c/p\u003e\n\u003ch2 id=\"9-mentioning-typefaces\"\u003e9. Mentioning Typefaces\u003c/h2\u003e\n\u003cp\u003eClients don\u0026rsquo;t care about typefaces. You don\u0026rsquo;t really want their input on this. Clients are uncomfortable in your field. The more you dive into these things, the more uncomfortable they\u0026rsquo;re going to get. And it\u0026rsquo;s going to look like you\u0026rsquo;re inviting them to do your job for them.\u003c/p\u003e\n\u003cp\u003eStop asking for permission to do a job you were hired for.\u003c/p\u003e\n\u003cp\u003eTheir comfort zone is their business needs, which is great since you are never going to be an expert on their domain. Talk about your work in terms of their business and needs.\u003c/p\u003e\n\u003ch2 id=\"10-talking-about-how-hard-you-worked\"\u003e10. Talking about how hard you worked\u003c/h2\u003e\n\u003cp\u003eYou\u0026rsquo;re not getting a grade on effort.\u003c/p\u003e\n\u003cp\u003eIf you do the work right, it will look like it was effortless.\u003c/p\u003e\n\u003ch2 id=\"11-reacting-to-questions-as-change-requests\"\u003e11. Reacting to questions as change requests\u003c/h2\u003e\n\u003cp\u003e\u0026ldquo;Why is this blue?\u0026rdquo;\u003c/p\u003e\n\u003cp\u003e\u0026ldquo;Oh I can change it.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eSometimes the client just has a question! They\u0026rsquo;re just looking for your reasoning.\u003c/p\u003e\n\u003ch2 id=\"12-not-guiding-the-feedback-loop\"\u003e12. Not guiding the feedback loop\u003c/h2\u003e\n\u003cp\u003eMost clients have no idea what kind of feedback you are looking for. They are not trained in your work. Anything that helps you do your job is part of your job. Know what you want before calling the presentation.\u003c/p\u003e\n\u003cp\u003eSome suggestions:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eHow well does this reflect your brand?\u003c/li\u003e\n\u003cli\u003eHow well does this reflect your users\u0026rsquo; needs as we discussed in the research?\u003c/li\u003e\n\u003cli\u003eHow well does this reflect your ad strategy?\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eThey\u0026rsquo;re not going to give it to you unless you explicitly ask for it.\u003c/p\u003e\n\u003ch2 id=\"13-asking-do-you-like-it\"\u003e13. Asking \u0026ldquo;Do you like it?\u0026rdquo;\u003c/h2\u003e\n\u003cp\u003eIf it had a like button on it would you push it?\u003c/p\u003e\n\u003cp\u003eAll the work you and your team did went down the drain! The client is no longer looking at you as an expert.\u003c/p\u003e\n\u003cp\u003eEvery decision on the project has been made with the benefit of expertise and data and you just uttered the most subjective phrase.\u003c/p\u003e\n\u003cp\u003eIf you want to make your client\u0026rsquo;s happy, make them successful.\u003c/p\u003e\n",
    "date_published": "2019.05.21",
    "tags": null,
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/haskellrealsequences/",
    "url": "https://brandonrozek.com/blog/haskellrealsequences/",
    "title": "Real Analysis Sequences in Haskell",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn Real Analysis it is useful to look at terms of a sequence. One of the best ways I\u0026rsquo;ve found to do this is in believe it or not Haskell. This is mainly for these two reasons\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eSupport for infinite data structures\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eBuilt-in Data Type to keep fractional precision\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"code\"\u003eCode\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s get started, first let us define a sequence by the following:\n$$\nf(1) = 1, f(2) = 2, f(n) = \\frac{1}{2}(f(n - 2) + f(n - 1))\n$$\nThat is equivalent to the following haskell code:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-haskell\" data-lang=\"haskell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ef\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e::\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eIntegral\u003c/span\u003e a \u003cspan style=\"color:#f92672\"\u003e=\u0026gt;\u003c/span\u003e a \u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eRatio\u003c/span\u003e a\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ef\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ef\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ef\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0.5\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e (f (n \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e f (n \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e))\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow to generate the sequence we just need to map $f$ onto the natural numbers.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-haskell\" data-lang=\"haskell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ensequence\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e map f [\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e..\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf you want to look at specific subsequences, such as even or odd:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-haskell\" data-lang=\"haskell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eodd_generator\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eodds\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e map odd_generator [\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e..\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eeven_generator\u003c/span\u003e n \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e n\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eevens\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e map odd_generator [\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e..\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo look at the differences between each term:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-haskell\" data-lang=\"haskell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ediff\u003c/span\u003e x \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e map (\u003cspan style=\"color:#a6e22e\"\u003e\\\u003c/span\u003e(a, b) \u003cspan style=\"color:#f92672\"\u003e-\u0026gt;\u003c/span\u003e a \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e b) \u003cspan style=\"color:#f92672\"\u003e$\u003c/span\u003e zip (tail x) (init x)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2019.05.21",
    "tags": ["Math","Haskell"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": true,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2018-01-22-identifying-misspelled-words-dataset-hunspell/",
    "url": "https://brandonrozek.com/blog/2018-01-22-identifying-misspelled-words-dataset-hunspell/",
    "title": "Identifying Misspelled Words in your Dataset with Hunspell",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis article is based on one written by \u003ca href=\"https://datascience.blog.wzb.eu/author/markus_konrad/\"\u003eMarkus Konrad\u003c/a\u003e at this link \u003ca href=\"https://datascience.blog.wzb.eu/2016/07/13/autocorrecting-misspelled-words-in-python-using-hunspell/\"\u003ehttps://datascience.blog.wzb.eu/2016/07/13/autocorrecting-misspelled-words-in-python-using-hunspell/\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eI assume in this article that you have hunspell and it\u0026rsquo;s integration with python installed. If not, please refer to the article mention above and follow the prerequisite steps.\u003c/p\u003e\n\u003cp\u003eThis article is inspired from the need to correct misspelled words in the \u003ca href=\"https://archive.ics.uci.edu/ml/datasets/Dresses_Attribute_Sales\"\u003eDress Attributes Dataset\u003c/a\u003e. I\u0026rsquo;ll share with you my initial pitfall, and what I ended up doing instead.\u003c/p\u003e\n\u003ch3 id=\"background-information\"\u003eBackground Information\u003c/h3\u003e\n\u003cp\u003eMisspelled words are common when dealing with survey data or data where humans type in the responses manually. In the Dress Attributes Dataset this is apparent when looking at the sleeve lengths of the different dresses.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edresses_data[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;SleeveLength\u0026#39;\u003c/span\u003e]\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003evalue_counts()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eWord\u003c/th\u003e\n          \u003cth\u003eFrequency\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003esleevless\u003c/td\u003e\n          \u003ctd\u003e223\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003efull\u003c/td\u003e\n          \u003ctd\u003e97\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eshort\u003c/td\u003e\n          \u003ctd\u003e96\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ehalfsleeve\u003c/td\u003e\n          \u003ctd\u003e35\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ethreequarter\u003c/td\u003e\n          \u003ctd\u003e17\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ethressqatar\u003c/td\u003e\n          \u003ctd\u003e10\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003esleeveless\u003c/td\u003e\n          \u003ctd\u003e5\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003esleeevless\u003c/td\u003e\n          \u003ctd\u003e3\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ecapsleeves\u003c/td\u003e\n          \u003ctd\u003e3\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ecap-sleeves\u003c/td\u003e\n          \u003ctd\u003e2\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ehalf\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ePetal\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eurndowncollor\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eturndowncollor\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003esleveless\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ebutterfly\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ethreequater\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eOuch, so many misspelled words. This is when my brain is racking up all the ways I can automate this problem away. Hence my stumbling upon Markus\u0026rsquo; post.\u003c/p\u003e\n\u003ch3 id=\"automagically-correcting-data\"\u003eAutomagically Correcting Data\u003c/h3\u003e\n\u003cp\u003eFirst, I decided to completely ignore what Markus warns in his post and automatically correct all the words in that column.\u003c/p\u003e\n\u003cp\u003eTo begin the code, let\u0026rsquo;s import and create an instance of the spellchecker:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e hunspell \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e HunSpell\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003espellchecker \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e HunSpell(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/usr/share/hunspell/en_US.dic\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/usr/share/hunspell/en_US.aff\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI modified his \u003ccode\u003ecorrect_words\u003c/code\u003e function so that it only corrects one word and so I can \u003ccode\u003eapply\u003c/code\u003e it along the \u003ccode\u003eSleeveLength\u003c/code\u003e column.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecorrect_word\u003c/span\u003e(checker, word, add_to_dict\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e[]):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Takes in a hunspell object and a word and corrects the word if needed\u0026#34;\u003c/span\u003e   \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Add custom words to the dictionary\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e w \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e add_to_dict:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        checker\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd(w)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    corrected \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# Check to see if it\u0026#39;s a string\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e isinstance(word, str):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e# Check the spelling\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        ok \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e checker\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003espell(word)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e ok:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#75715e\"\u003e# Grab suggestions for misspelled word\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            suggestions \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e checker\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esuggest(word)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e suggestions:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#75715e\"\u003e# Grab the best suggestion\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                best \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e suggestions[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                corrected \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e best\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#75715e\"\u003e# There are no suggestions for misspelled word, return the original\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                corrected \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e word \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#75715e\"\u003e# Word is spelled correctly\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            corrected \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e word\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e## Not a string. Return original\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        corrected \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e word\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e corrected\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow let\u0026rsquo;s apply the function over the \u003ccode\u003eSleeveLength\u003c/code\u003e column of the dataset:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edresses_data[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;SleeveLength\u0026#39;\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e dresses_data[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;SleeveLength\u0026#39;\u003c/span\u003e]\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eapply(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003elambda\u003c/span\u003e x: correct_word(spellchecker, x)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDoing so creates the following series:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eWord\u003c/th\u003e\n          \u003cth\u003eFrequency\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003esleeveless\u003c/td\u003e\n          \u003ctd\u003e232\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003efull\u003c/td\u003e\n          \u003ctd\u003e97\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eshort\u003c/td\u003e\n          \u003ctd\u003e96\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ehalf sleeve\u003c/td\u003e\n          \u003ctd\u003e35\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ethree quarter\u003c/td\u003e\n          \u003ctd\u003e17\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ethroatiness\u003c/td\u003e\n          \u003ctd\u003e10\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ecap sleeves\u003c/td\u003e\n          \u003ctd\u003e3\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ecap-sleeves\u003c/td\u003e\n          \u003ctd\u003e2\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ePetal\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ebutterfly\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eturndowncollor\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ehalf\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003elandownership\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eforequarter\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eAs you might be able to tell, this process didn\u0026rsquo;t go as intended. \u003ccode\u003elandownership\u003c/code\u003e isn\u0026rsquo;t even a length of a sleeve!\u003c/p\u003e\n\u003ch3 id=\"reporting-misspelled-items-and-allowing-user-intervention\"\u003eReporting Misspelled Items and Allowing User Intervention\u003c/h3\u003e\n\u003cp\u003eThis is when I have to remember, technology isn\u0026rsquo;t perfect. Instead we should rely on ourselves to identify what the word should be correctly spelled as.\u003c/p\u003e\n\u003cp\u003eKeeping that in mind, I modified the function again to take in a list of the data, and return a dictionary that has the misspelled words as the keys and suggestions as the values represented as a list.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edef\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003elist_word_suggestions\u003c/span\u003e(checker, words, echo \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eTrue\u003c/span\u003e, add_to_dict\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e[]):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Takes in a list of words and returns a dictionary with mispellt words as keys and suggestions as a list. Also prints it out\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e# add custom words to the dictionary\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e w \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e add_to_dict:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        checker\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd(w)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    suggestions \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e {}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e word \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e words:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e isinstance(word, str):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            ok \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e checker\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003espell(word)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e ok \u003cspan style=\"color:#f92672\"\u003eand\u003c/span\u003e word \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e suggestions:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                suggestions[word] \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e checker\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esuggest(word)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003enot\u003c/span\u003e suggestions[word] \u003cspan style=\"color:#f92672\"\u003eand\u003c/span\u003e echo:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    print(word \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;: No suggestions\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003eelif\u003c/span\u003e echo:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    print(word \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;[\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;, \u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ejoin(repr(i) \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e i \u003cspan style=\"color:#f92672\"\u003ein\u003c/span\u003e suggestions[word]), \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;]\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e suggestions\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWith that, I can use the function on my data. To do so, I convert the pandas values to a list and pass it to the function:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003es \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e list_word_suggestions(spellchecker, dresses_data[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;SleeveLength\u0026#39;\u003c/span\u003e]\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003evalues\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003etolist())\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThese are the suggestions it produces:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esleevless: [ \u0026#39;sleeveless\u0026#39;, \u0026#39;sleepless\u0026#39;, \u0026#39;sleeves\u0026#39;, \u0026#39;sleekness\u0026#39;, \u0026#39;sleeve\u0026#39;, \u0026#39;lossless\u0026#39; ]\r\nthreequarter: [ \u0026#39;three quarter\u0026#39;, \u0026#39;three-quarter\u0026#39;, \u0026#39;forequarter\u0026#39; ]\r\nhalfsleeve: [\u0026#39;half sleeve\u0026#39;, \u0026#39;half-sleeve\u0026#39;, \u0026#39;sleeveless\u0026#39; ]\r\nturndowncollor: No suggestions\r\nthreequater: [ \u0026#39;forequarter\u0026#39; ]\r\ncapsleeves: [ \u0026#39;cap sleeves\u0026#39;, \u0026#39;cap-sleeves\u0026#39;, \u0026#39;capsules\u0026#39; ]\r\nsleeevless: [ \u0026#39;sleeveless\u0026#39;, \u0026#39;sleepless\u0026#39;, \u0026#39;sleeves\u0026#39;, \u0026#39;sleekness\u0026#39;, \u0026#39;sleeve\u0026#39; ]\r\nurndowncollor: [ \u0026#39;landownership\u0026#39; ]\r\nthressqatar: [ \u0026#39;throatiness\u0026#39; ]\r\nsleveless: [ \u0026#39;sleeveless\u0026#39;, \u0026#39;levelness\u0026#39;, \u0026#39;valveless\u0026#39;, \u0026#39;loveless\u0026#39;, \u0026#39;sleepless\u0026#39; ]\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eFrom here, you can analyze the output and do the replacements yourself:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edresses_data[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;SleeveLength\u0026#39;\u003c/span\u003e]\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ereplace(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;sleevless\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;sleeveless\u0026#39;\u003c/span\u003e, inplace \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eTrue\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"whats-the-benefit\"\u003eWhat\u0026rsquo;s the Benefit?\u003c/h3\u003e\n\u003cp\u003eThis is where you ask \u0026ldquo;What\u0026rsquo;s the difference if it doesn\u0026rsquo;t automatically fix my data?\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eWhen you have large datasets, it can be hard to individually identify which items are misspelled. Using this method will allow you to have a list of all the items that are misspelled which can let you deal with it in a systematic way.\u003c/p\u003e\n",
    "date_published": "2018.01.22",
    "tags": [],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2017-08-28-obtaining-command-line-input-java/",
    "url": "https://brandonrozek.com/blog/2017-08-28-obtaining-command-line-input-java/",
    "title": "Obtaining Command Line Input in Java",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eTo obtain console input for your program you can use the \u003ccode\u003eScanner\u003c/code\u003e class\u003c/p\u003e\n\u003cp\u003eFirst import the relevant library\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e java.util.Scanner;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen create a variable to hold the \u003ccode\u003eScanner\u003c/code\u003e object\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eScanner input;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003einput \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e Scanner(System.\u003cspan style=\"color:#a6e22e\"\u003ein\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eInside the parenthesis, the \u003ccode\u003eScanner\u003c/code\u003e binds to the System input which is by default the console\u003c/p\u003e\n\u003cp\u003eThe new varible \u003ccode\u003einput\u003c/code\u003e now has the ability to obtain input from the console. To do so, use any of the following methods:\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eMethod\u003c/th\u003e\n          \u003cth\u003eWhat it Returns\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003enext()\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eThe next space separated string from the console\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003enextInt()\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eAn integer if it exists from the console\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003enextDouble()\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eA double if it exists from the console\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003enextFloat()\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eA float if it exists from the console\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003enextLine()\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eA string up to the next newline character from the console\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003ehasNext()\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eReturns true if there is another token\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003eclose()\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eUnbinds the Scanner from the console\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eHere is an example program where we get the user’s first name\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e java.util.Scanner;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eGetName\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estatic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e(String\u003cspan style=\"color:#f92672\"\u003e[]\u003c/span\u003e args) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Scanner input \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e Scanner(System.\u003cspan style=\"color:#a6e22e\"\u003ein\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    System.\u003cspan style=\"color:#a6e22e\"\u003eout\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eprint\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Please enter your name: \u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    String firstName \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e input.\u003cspan style=\"color:#a6e22e\"\u003enext\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    System.\u003cspan style=\"color:#a6e22e\"\u003eout\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eprintln\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Your first name is \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e firstName); \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2017.08.28",
    "tags": ["Java"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2017-08-28-escape-sequences-java/",
    "url": "https://brandonrozek.com/blog/2017-08-28-escape-sequences-java/",
    "title": "Escape Sequences in Java",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eSometimes you want to format your outputs. This is a quick cheatsheet containing the differerent escape sequences\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eCharacter\u003c/th\u003e\n          \u003cth\u003eEscape Sequence\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eNewline\u003c/td\u003e\n          \u003ctd\u003e\\n\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eTab\u003c/td\u003e\n          \u003ctd\u003e\\t\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eBackspace\u003c/td\u003e\n          \u003ctd\u003e\\b\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eDouble Quote\u003c/td\u003e\n          \u003ctd\u003e\u0026amp;#8221;\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eSingle Quote\u003c/td\u003e\n          \u003ctd\u003e\u0026amp;#8217;\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eBackslash\u003c/td\u003e\n          \u003ctd\u003e\\\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e \u003c/p\u003e\n",
    "date_published": "2017.08.28",
    "tags": ["Java"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/albuquerque/",
    "url": "https://brandonrozek.com/blog/albuquerque/",
    "title": "Albuquerque Real Estate Multiple Regression model",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003ch3 id=\"introduction\"\u003eIntroduction\u003c/h3\u003e\n\u003cp\u003eAlbuquerque, New Mexico is a city of thriving culture. The population has grown drastically within the past several years and is a hotspot for New Mexico. Albuquerque’s one-of-a-kind southwestern culture is all around the city, from the quaint shops, Pueblo and Spanish inspired architecture and world famous cuisine, to the music and art of the city. Being the largest city in the state, it has a wide variety of homes across the entire city whether it be within the actual city of Albuquerque ore in the outskirts. Many people flock to the city for the weather. It is a mildly dry climate with roughly 310 days of solid sunshine each year, making it a literal hotspot for people who enjoy the outdoors and warm weather. The city has little to no natural disasters that threaten everyday life, make it a safe place to settle. In the city of Albuquerque, there is a wide variety of living communities throughout the area. As mentioned above, there are different diverse communities. The housing availability is vast as well. The housing market in this region is vast. Because there is little to no potential for natural disasters it causes the market to grow due to less of a liability for increased damage from weather. Albuquerque, has one of the lowest costs of living than anywhere else in the country which makes it a great place to invest in the housing market. The goal of this project is to figure out details of real estate, specifically in the city of Albuquerque, New Mexico. In the dataset given, the predictors available are square footage, age of home, number of features, amount in taxes, and several binary variables. Those binary variables being whether or not the home was of a custom design, is located in a corner location, or if it is located in the northeast quadrant of the city.\u003c/p\u003e\n\u003ch3 id=\"descriptive-statistics\"\u003eDescriptive Statistics\u003c/h3\u003e\n\u003cp\u003eOur dataset features a representative sample of 117 homes in Albuquerque, New Mexico. From this dataset, we can describe the key patterns that arise and build an intuition for what is to come in our inferential statistics section. It is important to build this intuition since it helps when generalizing to a population.\u003c/p\u003e\n\u003ch4 id=\"distribution-of-data\"\u003eDistribution of Data\u003c/h4\u003e\n\u003cp\u003eTo understand the boxplot of interactions shown by Figure 1, we first need to establish what each of the indices or numbers under the x-axis represent. The first index discloses whether or not the home is located in the northeast quadrant of Albuquerque, New Mexico. The second 1index tells us whether or not the home had a custom design. And finally, the third index indicates whether or not the home is located in the corner of an intersection. From the boxplot of interactions, we can see that homes with a custom design tend to have a higher price than homes that don’t. There are no other patterns from this figure that we can readily pick up in terms of how the different combination of factors affect a home’s price. Figure 2 consists of multiple boxplots each comparing the price distribution of the different levels in all the categorical variables. From this we can reconfirm that having a custom design leads to a higher price distribution. We also see that the Northeast quadrant has a larger variation in price. There does not seem to be any significant difference in the price of homes depending on if it’s located on a corner. Finally, the more features a home has, the larger the variation in prices become. Figure 3 shows a scatterplot matrix of our data. From the matrix we can see the possibility of a linear correlation between price and square footage, as well as, price and tax. There is no evidence, however, of linear correlation between price and the age of the home. This suggests that perhaps age will not become a significant factor in our model. Figure 4 contains histograms of the different quantitative variables. As shown in the figure, the histograms are all right skewed. This shows that the majority of homes are in the lower price range while there are a few homes in the upper price region that might skew our model. Figure 5 shows the distribution of the data in terms of the categorical variables. It is interesting to note that the number of features falls in a gaussian like curve which is unimodal and symmetric at about 4 features. There are more homes that do not have a custom design and do not lie on a corner than ones that do. In this dataset, there are also more homes in the northeast quadrant than homes that are not. Figure 6 shows the distribution of taxes to be right skewed as well.\u003c/p\u003e\n\u003ch4 id=\"effectinteraction-plots\"\u003eEffect/Interaction Plots\u003c/h4\u003e\n\u003cp\u003eIn the following section we will consider the effect of different categorical variables on the price of homes in Albuquerque and the other variables. The effect plots are used primarily to build an intuition on an individual variable’s effect on the price of a home. Interaction plots, however, analyze the influence of a variable on another’s effect on the price of a home. If the lines in an interaction plot intersect, it tells us that a categorical variable has a significant influence on another’s effect on the price of a home. The main effect plots in Figure 7 shows us the mean price of a home in relation to the different levels for each of the factors in the dataset. Similar to what was discovered in the boxplot figures, having a custom design increases the price of a home drastically compared to the increase of the price of a home by other factors. Having a home in a non-corner location slightly decreases the value of a home on average. Finally, it is shown in the Feature Effect plot that increases in the number of features increases the price of a home. An interesting thing to note is how in the case of 8 features, the average price of a home decreases. Figures 8, 9, and 10 show the interaction plots of different categorical variable combinations. In Figures 8 and 9, the lines do not intersect, therefore we can conclude that being in the northeast bears no influence on how custom design influences the price of the home and being in the northeast also bears no influence on how the corner location affect on the price. Figure 10, however, shows an interaction between corner location and custom design. This suggests that corner location has an influence on how custom design effects the price.\u003c/p\u003e\n\u003ch4 id=\"missing-data\"\u003eMissing Data\u003c/h4\u003e\n\u003cp\u003eIn the dataset provided, 44% of the homes are missing some data. Missing data, like outliers, are not to be ignored. To deal with missing data, one needs to know the implications of missing data on the analysis. From there, we can think of several techniques to deal with the absence of the data. One of the most important implications to check is to see if the data is missing at random. If the data is not missing at random, then it taints the representability of the sample, making it harder to generalize to a population. In the dataset provided there are two variables that contain missing data. Figure 11 shows how 42% of the data is missing the age variable and 9% of the data is missing the Tax variable. Looking at the right side of Figure 11, we can see the pattern of missing data in our set. 35% of the data in our set is only missing the age variable, 7% of our data is missing the age and tax variable, and only 2% of our data is missing the tax variable alone. Figure 12 shows us the missing data with respect to quantitative variables such as price, square footage, and tax. As seen in the figure, the missing data is spread equally throughout the distribution. Figure 13 shows us the missing data with respect to the categorical variables in our dataset. As seen in the figure, the proportion of missing data in the binary variables are about the same for each level. When looking at the number of features, however, there is a higher proportion of data missing in the homes with fewer features than in the homes with more features. Looking at the scatterplot matrix of missing values in Figure 14, we can see that the missing values in red are spread out evenly through the observed values in blue. Missing data with respect to age was not considered since the majority of the missing data comes from the age variable.\u003c/p\u003e\n\u003ch3 id=\"inferential-statistics\"\u003eInferential Statistics\u003c/h3\u003e\n\u003cp\u003eNow that we’ve described the sample, we can now look forward to generalizing our suspicions to the population. In this section, we define the hypotheses of interest in our model and then consider different techniques that lead up to the final model and analysis.\u003cbr\u003e\nHypotheses The main hypothesis of interest is if the model is significant.\u003cbr\u003e\n➢ H​ 0​ : There is no linear association between the price of homes in Albuquerque, New Mexico and any of the following: square footage, age of the home, tax, number of features, corner location, northeast location, or custom design.\u003cbr\u003e\n➢ H​ A​ : There is a linear association between the price of homes in Albuquerque, New Mexico and at least one of the following predictors: square footage, age of the home, tax, number of features, corner location, northeast location, or custom design. Afterwards, we consider if square footage, age, tax, number of features, corner location, northeast location, or custom design are significant predictors in the model individually.\u003c/p\u003e\n\u003ch4 id=\"ignore-missing-data-technique\"\u003eIgnore Missing Data Technique\u003c/h4\u003e\n\u003cp\u003eOne of the first regression models we considered is if we ignore all of the missing data. Since 44% of our data have missing values, this drops our degrees of freedom significantly to 61. This technique also doesn’t utilize all of the data, which is prefered so that it doesn’t affect the coefficients of our model. Running the stepwise algorithm to maximize the AIC, we obtain the following model\u003cbr\u003e\nPRICE = 97.92321 + 0.33638(SQFT) + 0.52308(TAX) + 177.18519(CUST1) − 77.82234(COR1)\u003cbr\u003e\nWhere the p-value of the model is less than 2.2e-16. At a 5% significance level, there is a linear association between the price of the home and at least one of the following predictors: square footage, tax, custom design, or corner location. Looking at the individual t-tests for predictor significance, square footage, tax, and custom design all have a p-value around or less than 0.01 which rejects the null hypotheses of those predictors. This tells us that square footage, tax, and custom design are all significant predictors in our model. However, corner location only have a p-value of 0.09, which at a 5% significant level, fails to reject the null hypothesis. Corner location is not a significant predictor in our model. The residual standard error in this model is $155,400 which is about 15% of the mean price value of a home. The adjusted R​ 2 value is 0.8523, which means that about 85% of the variation in the prices are accounted for in the variation of the model. Not all of the conditions are met for this model and since we ignore a good chunk of the data, this model is not fully appropriate for our usage. In our next attempt, we will increase the number of observations that we include in our analysis.\u003c/p\u003e\n\u003ch4 id=\"remove-age-column-technique\"\u003eRemove Age Column Technique\u003c/h4\u003e\n\u003ch5 id=\"justification-of-removal-of-age-variable\"\u003eJustification of Removal of Age Variable\u003c/h5\u003e\n\u003cp\u003eMost of the data missing is in the age variable. Age is also not a significant predictor in the model made in the previous section, therefore, our next idea was to remove the age variable from the dataset. It is important, however, to establish that the age data is truly missing at random and removing it won’t significantly affect the analysis. We do this by removing the only other variable that has missing data (tax) from the dataset. Looking at Figures 17 and 18, it did not change significantly the distribution of missing data with respect to the different variables. This lets us comfortably remove the age column from our dataset. From here, we then repeat the Ignore Missing Data technique. This time since only 9% of the data is missing, our degrees of freedom in this analysis increases from 61 to 102. Having a greater degrees of freedom allows us to have more statistical power in our analysis.\u003c/p\u003e\n\u003ch5 id=\"variable-selection-and-test-results\"\u003eVariable Selection and Test Results\u003c/h5\u003e\n\u003cp\u003eRunning through the stepwise algorithm to maximize AIC, we get the following model\u003cbr\u003e\nPRICE = 175.16603 + 0.67707(TAX) + 156.81481(CUST1) + 0.20760(SQFT) − 83.40126(COR1)\u003cbr\u003e\nThe p-value of this model is less than 2.2e-16, which at a 5% significance level, leads us to reject the null hypothesis and state that price has a linear association between at least one of the predictors in the model. Looking at the t-tests for predictor significance, all of the p-values are less than 0.05, which leads us to reject all the null hypotheses, and state that all of the predictors in this model: tax, custom design, square footage, and corner location are significant. The residual standard error in this model is $162,300 which is slightly higher from the $155,400 in the previous model. Though the proportion that this residual standard error is of the mean of the price set is the same at 15%. The adjusted R​ 2 value is 0.8213, which means that 82% of the variation in the prices are accounted for by the variation in the model. This dropped from the previous 85% value from the\u003cbr\u003e\nprevious model. The residual standard error and adjusted R​ 2 values indicate that this model is worse in terms of its predictive ability. This model, however, takes more of the data into account, which makes it more accurate when considering the data as a whole and not the subsetted data that arrives from removing the null entries. In this model, we still lose about 9% of our data. In our next model, we will consider a technique that allows us to use the entire dataset.\u003c/p\u003e\n\u003ch4 id=\"multiple-imputation\"\u003eMultiple Imputation\u003c/h4\u003e\n\u003cp\u003eTo take the entire dataset into account we need to decide how to handle the missing values. Multiple imputation is a technique that randomly samples from the existing distribution to take place of the missing data multiple times. The benefits of multiple imputation is that it preserves the mean and variance of the data. For multiple imputation to work, the data must be missing at\u003cbr\u003e\nrandom. In the missing data section, we described how the missing data is spread equally with respect to the different variables. This allows us to say that the Missing at Random condition is met.\u003c/p\u003e\n\u003ch5 id=\"variable-selection-and-test-results-1\"\u003eVariable Selection and Test Results\u003c/h5\u003e\n\u003cp\u003eRunning the stepwise algorithm to maximize the AIC value, we get the following model\u003cbr\u003e\nPRICE = 138.09421 + 0.65373(TAX) + 0.23709(SQFT) + 124.31776(CUST2) − 6 7.17475(COR2)\u003cbr\u003e\nFrom the F-Test, we can reject the null hypothesis at a 5% significance level due to the fact that the p-value is less than 2.2e-16. This means that there is a linear association between price and one of the predictors: tax, square footage, custom design, and corner office. All of the T-Tests for predictor significance aside from COR2 have a p-value of less than 0.05. Meaning, we reject the appropriate null hypotheses and state that tax, square footage, and custom design are significant predictors. The residual standard error is $167,700 with 112 degrees of freedom. This value corresponds to 16% of the mean price of a home. The adjusted R​ 2 value is 0.8056, which means that about 81% of the variation in the prices is accounted for by this model. The residual standard error and adjusted R​ 2 values are again worse than the previous model. However, since this model uses the entire dataset, we can say that this is a more representative sample of the data than the previous models. To lower the amount of error in this model, we considered removing the outliers. In this model there are six outliers as shown in Figure 19. Since they are significantly far from the middle 50% of the price distribution, we removed them in the following section to better make a model that describes the main portion of the data.\u003c/p\u003e\n\u003ch4 id=\"multiple-imputation-without-outliers\"\u003eMultiple Imputation without Outliers\u003c/h4\u003e\n\u003cp\u003eRemoving the outliers from the model and running the stepwise algorithm to maximize AIC, we obtain\u003cbr\u003e\nPRICE = 76.47917 + 0.64130(TAX) + 0.27290(SQFT) + 77.58816(CUST2)\u003cbr\u003e\n6For the F-Statistic, the p-value is less than 2.2e-16. At a 5% significance level, we reject the null hypothesis and state that there is a linear association between price and at least one of the following: tax, square footage, or custom design. The p-values of the T-Tests for predictor significance tells us that at a 5% significant level. all of the predictors are significant since all of the p-values are less than 0.05. The residual standard error of this model is $112,700 which is 11% of the mean of the dataset.\u003cbr\u003e\nThe adjusted R​ 2 value is 0.8901, which means that 89% of the variation in the prices is accounted for by the variation of the model. In this model, the residual standard error is lower and the adjusted R​ 2 value is higher than the previous models. This tells us that it is a better fit for the data without the outliers. Looking at the outlier effect on the coefficients, we can see that without the outliers the term relating to the corner location goes away from the model. That then significantly changes the\u003cbr\u003e\ncoefficients of CUST2 and the y-intercept.\u003c/p\u003e\n\u003ch5 id=\"checking-the-conditions-for-inference\"\u003eChecking the Conditions for Inference\u003c/h5\u003e\n\u003cp\u003eBefore we conclude with the analysis, we must first check the conditions for inference to see if the technique is appropriate for our data.\u003cbr\u003e\n\u003cstrong\u003eIndependence Assumption:\u003c/strong\u003e\nA house’s selling price can depend on another’s so this condition is not met.\u003cbr\u003e\n\u003cstrong\u003eRandomization Condition:\u003c/strong\u003e\nThe dataset is comprised of a random sample of records of resale of homes which satisfies the\u003cbr\u003e\nrandomization condition.\u003cbr\u003e\n\u003cstrong\u003eStraight Enough Condition:\u003c/strong\u003e\nThe scatterplot matrix in Figure 20 shows that for the predictors square footage and tax that the\u003cbr\u003e\nscatterplot is straight enough and doesn’t have any bends or curves.\u003cbr\u003e\n\u003cstrong\u003eEqual Variance Assumption:\u003c/strong\u003e\nThe residual analysis in Figure 21 shows that the outliers are not spread equally on the\u003cbr\u003e\nscatterplot. Therefore, the equal variance assumption is not met.\u003cbr\u003e\n\u003cstrong\u003eNearly Normal Condition:\u003c/strong\u003e\nThe QQ-Plot in Figure 21 shows that the residuals follow a unimodal and symmetric distribution.\u003cbr\u003e\nTaking out the outliers in the model also did not introduce any new outliers in the boxplot.\u003cbr\u003e\n\u003cstrong\u003eMissing At Random Condition:\u003c/strong\u003e\n7The discussion in the descriptive statistics section about the missing data tells us that the data\u003cbr\u003e\nis missing evenly with respect to the different variables. Therefore, it is safe to assume that the\u003cbr\u003e\ndata is missing at random\u003cbr\u003e\n\u003cstrong\u003eMulticollinearity Condition:\u003c/strong\u003e\nAll of the VIF values are lower than 10, therefore this condition is met.\u003c/p\u003e\n\u003cp\u003eThe conditions for inference are not fully met due to the equal variance assumption. This means that our model will be more inaccurate for some price range of homes than others. Looking at our residual analysis, it appears that the inaccuracies happen when the price of the home is higher. There weren’t many outliers in the dataset (6 out of 117 or 5%) so removing these outliers makes the model more representative to the majority of the houses in the market. Since this model is intended to be used when analyzing prices of homes in the area, it is better not to include the outliers that most people don\u0026rsquo;t intend to buy. Since the error term is unimodal and symmetric, we can be at ease that there isn’t any other confounding factor in our model. Overall, this is a good model to use for inference and prediction as long as one doesn’t use it to describe the outliers.\u003c/p\u003e\n\u003ch3 id=\"conclusion\"\u003eConclusion\u003c/h3\u003e\n\u003cp\u003eThe multiple imputation model without outliers is the best model outlined in this paper for describing the price of housing in this region. The formula is re-expressed here\u003cbr\u003e\nPRICE = 76.47917 + 0.64130(TAX) + 0.27290(SQFT) + 77.58816(CUST2)\u003cbr\u003e\nThis states that for every dollar of tax spent on the home, the home increases on average by $64 given the other parameters stay constant. The same concept applies to square footage and custom design. For every square foot added to the home, the value of it increases on average by $27. Having a home with a custom design increases the value of the home by $7700. This model is more reliable the lower the price of the home is. When it comes to high cost homes, the error produced by the model increases. From this model, we conclude that property tax, square footage, and whether or not a home is built from a custom design are the most significant factors in the price of a home in Albuquerque, New Mexico.\u003c/p\u003e\n",
    "date_published": "2017.07.12",
    "tags": ["Statistics"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2017-06-05-java-swing-components/",
    "url": "https://brandonrozek.com/blog/2017-06-05-java-swing-components/",
    "title": "Java Swing Components",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis post, over time, will serve as a reference to myself and others of the different UI components available in the Swing library. This post assumes a general familiarity with setting up a basic Swing application and focuses only on the individual components.\u003c/p\u003e\n\u003ch3 id=\"buttons\"\u003eButtons\u003c/h3\u003e\n\u003cp\u003eButtons are created using the JButton component. The constructor takes the text placed inside the button.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eJButton stopBtn \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JButton(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Stop\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/stopbutton.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eYou can also add images inside a button. To do that you need to get the image and make it into an icon. The following code grabs the image file “smallpanda.jpg” from the current working directory.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eImage img \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003egetImage\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003egetCodeBase\u003c/span\u003e(), \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;smallpanda.jpg\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eImageIcon imgIcon \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e ImageIcon(img);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eJButton feedBtn \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JButton(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Feed\u0026#34;\u003c/span\u003e, imgIcon);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSometimes, you want to change the location of the text in the button. Like say, we want to place the text in the center horizontally and bottom vertically.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efeedBtn.\u003cspan style=\"color:#a6e22e\"\u003esetHorizontalTextPosition\u003c/span\u003e(JButton.\u003cspan style=\"color:#a6e22e\"\u003eCENTER\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efeedBtn.\u003cspan style=\"color:#a6e22e\"\u003esetVerticalTextPosition\u003c/span\u003e(JButton.\u003cspan style=\"color:#a6e22e\"\u003eBOTTOM\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDon\u0026rsquo;t forget to add your buttons to the screen!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(stopBtn);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(feedBtn);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/smallpandabutton.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003ch3 id=\"labels-and-textfields\"\u003eLabels and Textfields\u003c/h3\u003e\n\u003cp\u003eOne of the most common forms of input is a text field, usually distinguished with a label. Those components are called JTextField and JLabel respectively. The constructor for JTextArea can take just the width of the text field, or another common use is to have already inputed text and its width.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    JLabel nameLabel \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JLabel(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Enter in your name: \u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// Create an input and set the width to be 10px wide\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    JTextField nameInput \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JTextField(10);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e//Override nameInput with a field already contains the text \u0026#34;Brandon\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e//And is 10px wide\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    nameInput \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JTextField(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e, 10);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(nameLabel);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(nameInput);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/labeltextfield.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003ch3 id=\"checkboxes\"\u003eCheckboxes\u003c/h3\u003e\n\u003cp\u003eCheckboxes are commonly used when giving the possibility for multiple answers. Such as, check all of the foods that you like.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    JCheckBox pizza \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JCheckBox(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Pizza\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    JCheckBox noodles \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JCheckBox(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Noodles\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    JCheckBox rice \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JCheckBox(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Rice\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(pizza);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(noodles);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(rice);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/checkboxes.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eYou can even replace the default look of the checkbox with an image. To do this, you need to make image icons for both when it\u0026rsquo;s checked and when it\u0026rsquo;s unchecked.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eImage checkedImage \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003egetImage\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003egetCodeBase\u003c/span\u003e(), \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;checked.png\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eImage uncheckedImage \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003egetImage\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003egetCodeBase\u003c/span\u003e(), \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;unchecked.png\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eImageIcon checkedIcon \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e ImageIcon(checkedImage);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eImageIcon uncheckedIcon \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e ImageIcon(uncheckedImage);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eJCheckBox checkbox \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JCheckBox(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Check Me\u0026#34;\u003c/span\u003e, uncheckedIcon);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echeckbox.\u003cspan style=\"color:#a6e22e\"\u003esetSelectedIcon\u003c/span\u003e(checkedIcon);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(checkbox);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/unchecked.png\" alt=\"\"\u003e\n\u003cimg src=\"/files/images/blog/checked.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003ch3 id=\"text-areas\"\u003eText Areas\u003c/h3\u003e\n\u003cp\u003eText Areas are different from text fields in which it is made to be able to hold multiple lines of text. It\u0026rsquo;s called JTextArea and its construction takes a width and height as it\u0026rsquo;s arguments.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eJTextArea textarea \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JTextArea(10, 10);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBy default, when the someone inputs more text than the size can hold, it will automatically grow with the text inputted. To override this behaviour and instead introuduce scroll bars. One must define a ScrollPane and put the TextArea inside of it by using it as the scroll pane\u0026rsquo;s argument for its constructor.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eJScrollPane scrollPane \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JScrollPane(textarea);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/textarea.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003ch3 id=\"radio-buttons\"\u003eRadio Buttons\u003c/h3\u003e\n\u003cp\u003eRadio buttons are used for when you only want one out of many different options to be selected. For this, one needs to define a button group that houses the radio buttons for the user to choose from. This can be achieved with ButtonGroup and JRadioButton respectively.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Make the radio buttons\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eJRadioButton radio1 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JRadioButton(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Pies\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eJRadioButton radio2 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JRadioButton(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Cakes\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eJRadioButton radio3 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JRadioButton(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Cookies\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Put the radio buttons in a group\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eButton Group desserts \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e ButtonGroup();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edesserts.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(radio1);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edesserts.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(radio2);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edesserts.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(radio3);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Add the radio buttons to the screen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(radio1);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(radio2);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(radio3);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/radiobuttons.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003ch3 id=\"jlist\"\u003eJList\u003c/h3\u003e\n\u003cp\u003eTo display a list of items that are clickable by the user, you can use a \u003ccode\u003eJList\u003c/code\u003e. JLists require a model that stores the list implementation, we\u0026rsquo;ll use \u003ccode\u003eDefaultListModel\u003c/code\u003e to achieve this purpose.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eDefaultListModel model \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e DefaultListModel();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eJList list \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JList(model);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo add scrolling capabilities, remember to add it to a scroll pane\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eJScollPane sp \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JScrollPane(list);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can set the number of items you wish to see in the list. The example below, allows us to see three items in the list.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elist.\u003cspan style=\"color:#a6e22e\"\u003esetVisibleRowCount\u003c/span\u003e(3);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThere are a variety of ways to add items to the list. If a number is specified that tells it to place it at the index specified. Starting from the top at zero, to the button.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emodel.\u003cspan style=\"color:#a6e22e\"\u003eaddElement\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Apples\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emodel.\u003cspan style=\"color:#a6e22e\"\u003eaddElement\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Cherries\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emodel.\u003cspan style=\"color:#a6e22e\"\u003eaddElement\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Bananas\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Adds \u0026#39;Oranges\u0026#39; to the top\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emodel.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(0, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Oranges\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSometimes, you want to only let the user select one item. At the end, don\u0026rsquo;t forget to add the component to the screen!\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003elist.\u003cspan style=\"color:#a6e22e\"\u003esetSelectionMode\u003c/span\u003e(ListSelectionModel.\u003cspan style=\"color:#a6e22e\"\u003eSINGLE_SELECTION\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(sp);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/JList.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003ch3 id=\"jcombobox\"\u003eJComboBox\u003c/h3\u003e\n\u003cp\u003eTo create a dropdown list of different options, consider using a JComboBox.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eJComboBox cb \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e JComboBox();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecb.\u003cspan style=\"color:#a6e22e\"\u003eaddItem\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Select Food Option\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecb.\u003cspan style=\"color:#a6e22e\"\u003eaddItem\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Pizza\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecb.\u003cspan style=\"color:#a6e22e\"\u003eaddItem\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Burger\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecb.\u003cspan style=\"color:#a6e22e\"\u003eaddItem\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Hot Dog\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecb.\u003cspan style=\"color:#a6e22e\"\u003eaddItem\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Steak\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Add it to the screen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(cb);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/JComboBox.png\" alt=\"\"\u003e\u003c/p\u003e",
    "date_published": "2017.06.05",
    "tags": ["Java"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2017-06-05-using-system-themes-java-swing/",
    "url": "https://brandonrozek.com/blog/2017-06-05-using-system-themes-java-swing/",
    "title": "Using System Themes In Java Swing",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThe default theme for Java Swing components is a cross-platform theme called “Metal”. I use the Adapta theme for GTK on Linux and this theme does not match at all what my other GUI applications look like. So here, I will describe a simple way to utlize already existent system themes in Java Swing applications.\u003c/p\u003e\n\u003ch3 id=\"solution\"\u003eSolution\u003c/h3\u003e\n\u003cp\u003eIn the init method of your java application, place the following code.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003etry\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    UIManager.\u003cspan style=\"color:#a6e22e\"\u003esetLookAndFeel\u003c/span\u003e(UIManager\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                               .\u003cspan style=\"color:#a6e22e\"\u003egetSystemLookAndFeelClassName\u003c/span\u003e());\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e} \u003cspan style=\"color:#66d9ef\"\u003ecatch\u003c/span\u003e(Exception e) {}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHere the application will attempt to look up the system theme and set that as the default styles for the Swing components. If the lookup fails, then it will default back to the metal theme.\u003c/p\u003e\n\u003cp\u003eFor more information, check out this page from \u003ca href=\"http://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html\"\u003eOracle\u003c/a\u003e.\u003c/p\u003e\n\u003ch3 id=\"discussion\"\u003eDiscussion\u003c/h3\u003e\n\u003cp\u003eIf it is so easy to set up applications that look native to each desktop environment, why not have that by default? With the cross platform metal theme, you can ensure that the style of your application is the same across all the operating systems. In this fashion, you don\u0026rsquo;t need to worry about spacing between components and have full control of the “look and feel” of your application.\u003c/p\u003e\n\u003cp\u003eSince I am used to development for the web, I don\u0026rsquo;t have strong motivation to have an application look the same on all platforms. I prefer the application to match the system theme and look like it was built for the platform that I am on. One loses partial control on the presentation of your application across different desktop environmnets, but with a strong layout, it is possible to make it look organized and integrated.\u003c/p\u003e",
    "date_published": "2017.06.05",
    "tags": ["Java"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2017-05-24-viewing-java-applets/",
    "url": "https://brandonrozek.com/blog/2017-05-24-viewing-java-applets/",
    "title": "Viewing Java Applets",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eWhen you use an IDE there are many things you can take for granted. A section of an intro level computer science course at my university uses \u003ca href=\"http://www.jgrasp.org/\"\u003eJGrasp\u003c/a\u003e to build Java Applets.\u003c/p\u003e\n\u003cp\u003eFollowing around using a normal text editor, I found that I couldn\u0026rsquo;t just compile and run the code like I have with my java programs in the past. To be able to help around and assist in the course, I need to be able to build and run these applications. The rest of this article describes the process I underwent to be able to use my existing setup to write and build java applets. Of course you can always install JGrasp and have that all built in, but it\u0026rsquo;s always nice to not have to change your workflow.\u003c/p\u003e\n\u003cp\u003eWhen I tried following along, I would receive the following error\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e    Main method not found in class HelloWorld, please define main method as...\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eWhich makes sense since I have never defined a main method inside my source code. So how do I go about doing this?\u003c/p\u003e\n\u003ch2 id=\"setup\"\u003eSetup\u003c/h2\u003e\n\u003cp\u003eJava Applets are meant to run on web pages, because of this one needs an html file to host the applet. The following code gives you the bare minimum for setting up the html file. I called it \u003ccode\u003eHelloWorld.html\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003ehtml\u003c/span\u003e\u0026gt;;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003ehead\u003c/span\u003e\u0026gt;\u0026lt;\u003cspan style=\"color:#f92672\"\u003etitle\u003c/span\u003e\u0026gt;Applet Container\u0026lt;/\u003cspan style=\"color:#f92672\"\u003etitle\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;\u003cspan style=\"color:#f92672\"\u003ebody\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u0026lt;\u003cspan style=\"color:#f92672\"\u003eapplet\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;HelloWorld.class\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ewidth\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e400\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eheight\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e400\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003eapplet\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u0026lt;/\u003cspan style=\"color:#f92672\"\u003ebody\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ehtml\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"hello-world-program\"\u003eHello World Program\u003c/h2\u003e\n\u003cp\u003eTo get it up and running, I will show a “Hello World” like application for applets.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e javax.swing.JApplet;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e java.awt.Graphics;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eHelloWorld\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eextends\u003c/span\u003e JApplet {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003epaint\u003c/span\u003e(Graphics g) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        g.\u003cspan style=\"color:#a6e22e\"\u003edrawString\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e, 30, 30);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e} \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"running-the-applet\"\u003eRunning the Applet\u003c/h2\u003e\n\u003cp\u003eNow we need to compile the code\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ejavac HelloWorld.java\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen run the appletviewer\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eappletviewer HelloWorld.html\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eThis tutorial concludes the setup of running a simple Java applet. From here you can look at the different methods in the \u003ca href=\"https://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html\"\u003eGraphics library\u003c/a\u003e and play around 😀\u003c/p\u003e\n",
    "date_published": "2017.05.24",
    "tags": ["Java"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/male-vs-female-life-expectancy/",
    "url": "https://brandonrozek.com/blog/male-vs-female-life-expectancy/",
    "title": "Male vs Female Life Expectancy",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cimg src=\"/files/images/blog/LifeExpectancyBoxplot.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003ch2 id=\"do-females-live-longer-than-males\"\u003eDo females live longer than males?\u003c/h2\u003e\n\u003cp\u003eIt is well known that females live longer than males, but does that statement hold statistically? Matthew Martinez and I set out to find out.\u003c/p\u003e\n\u003ch2 id=\"population-and-the-hypothesis\"\u003ePopulation and the hypothesis\u003c/h2\u003e\n\u003cp\u003eOur population of concern is citizens of the United States. We found a dataset on \u003ca href=\"http://www.worldlifeexpectancy.com/\"\u003eWorldLifeExpectancy\u003c/a\u003e listing by county the average life expectancy for both males and females. With this we form our null and alternative hypothesis\u003c/p\u003e\n\u003cp\u003eH0: The average life expectancy for both males and females are the same in the United States\u003c/p\u003e\n\u003cp\u003eHA: The average female life expectancy is higher than the average male life expectancy in the United States\u003c/p\u003e\n\u003ch2 id=\"data-preparation\"\u003eData preparation\u003c/h2\u003e\n\u003cp\u003eSince the website gives us an overlook at all of the counties in the United States we want to take a small sample of that so we can perform statistics. Using the entire dataset will result in looking at population parameters which doesn\u0026rsquo;t leave room for inference.\u003c/p\u003e\n\u003cp\u003eA random number was chosen to pick the state and then the county. This was done a total of 101 times. The CSV file is located \u003ca href=\"/data/LifeExpectancy.csv\"\u003ehere\u003c/a\u003e for convenience.\u003c/p\u003e\n\u003ch2 id=\"r-programming\"\u003eR Programming\u003c/h2\u003e\n\u003cp\u003eFor the rest of this article, we will use R for analysis. This article will focus more on the analysis, however, than the R code.\u003c/p\u003e\n\u003cp\u003eRead the CSV file into R\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003erm\u003c/span\u003e(list\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003els\u003c/span\u003e())\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Read in file\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLifeExpectancy \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eread.csv\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;~/LifeExpectancy.csv\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emaleExpectancy \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LifeExpectancy\u003cspan style=\"color:#f92672\"\u003e$\u003c/span\u003eLife.Expectancy.Male\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efemaleExpectancy \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LifeExpectancy\u003cspan style=\"color:#f92672\"\u003e$\u003c/span\u003eLife.Expectancy.Female\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"summary-statistics\"\u003eSummary Statistics\u003c/h2\u003e\n\u003cp\u003eBefore we begin our inferential statistics, it is a good idea to look at what we have in our sample. It will give us a good feeling for what we\u0026rsquo;re working with and help us answer some questions involving the assumptions in parametric tests.\u003c/p\u003e\n\u003cp\u003eWe\u0026rsquo;re interested in the minimum, mean, maximum, and interquartile range of the data\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Summary statistics\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emale_row \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ec\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003emin\u003c/span\u003e(maleExpectancy), \u003cspan style=\"color:#a6e22e\"\u003emean\u003c/span\u003e(maleExpectancy), \u003cspan style=\"color:#a6e22e\"\u003emax\u003c/span\u003e(maleExpectancy), \u003cspan style=\"color:#a6e22e\"\u003eIQR\u003c/span\u003e(maleExpectancy))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efemale_row \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ec\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003emin\u003c/span\u003e(femaleExpectancy), \u003cspan style=\"color:#a6e22e\"\u003emean\u003c/span\u003e(femaleExpectancy), \u003cspan style=\"color:#a6e22e\"\u003emax\u003c/span\u003e(femaleExpectancy), \u003cspan style=\"color:#a6e22e\"\u003eIQR\u003c/span\u003e(femaleExpectancy))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esummary \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erbind\u003c/span\u003e(male_row, female_row)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ecolnames\u003c/span\u003e(summary) \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ec\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Min\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Mean\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Max\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;IQR\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003erownames\u003c/span\u003e(summary) \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ec\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Male\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Female\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLooking at the table below, we can see that the average male lives to be around 69 years old in our sample while the average female lives to be about 71 years old. One interesting thing to note is how small the variation is between all the counties life expectancy that we sampled.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esummary\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e##         Min   Mean  Max   IQR\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## Male   69.0 74.952 80.9 2.775\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## Female 76.1 80.416 84.1 2.350\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"inferential-statistics\"\u003eInferential Statistics\u003c/h2\u003e\n\u003cp\u003eFrom here on out, we will perform a hypothesis test on the two hypothesis stated earlier in the text.\u003c/p\u003e\n\u003cp\u003eSince our data is quantitative in nature, we will attempt to perform a two sample t-test\u003c/p\u003e\n\u003ch3 id=\"check-for-assumptions\"\u003eCheck for Assumptions\u003c/h3\u003e\n\u003cp\u003ePerforming a t-test comes with several assumptions we need to check before confidently reporting our results.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eIndependence Condition:\u003c/strong\u003e One county\u0026rsquo;s life span does not affect the lifespan of another.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eIndependent groups assumption:\u003c/strong\u003e The lifespan of a male does not directly impact a lifespan of a female.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eNearly Normal Condition:\u003c/strong\u003e We need to check the histograms to see if they\u0026rsquo;re unimodal and symmetric and check to see if any outliers exist\u003c/p\u003e\n\u003cp\u003eThe male life expectancy distribution appears to be unimodal and symmetric.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check for normality\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ehist\u003c/span\u003e(maleExpectancy, main \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Male Life Expectancy\u0026#34;\u003c/span\u003e, xlab \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Age\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/maleLifeExpectancyHist.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eSame with the female life expectancy distribution\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ehist\u003c/span\u003e(femaleExpectancy, main \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Female Life Expectancy\u0026#34;\u003c/span\u003e, xlab \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Age\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/femaleLifeExpectancyHist.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eLooking at the boxplot, we can see that the IQR of the female life expectancy is higher than the one of the males. The hypothesis test will show us if this is of significant difference. On the male\u0026rsquo;s side there are two outliers. This violates the Nearly Normal Condition so we must proceed with caution in our test.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eboxplot\u003c/span\u003e(maleExpectancy, femaleExpectancy, names \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ec\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Male Life Expectancy\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Female Life Expectancy\u0026#34;\u003c/span\u003e), ylab \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Age\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/LifeExpectancyBoxplot.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eSince the nearly normal condition was not met, we do not meet the assumptions necessary to perform a t-test. However, since the condition was violated by an outlier, let us perform a t-test with the outlier and without the outlier and compare the results.\u003c/p\u003e\n\u003ch3 id=\"calculate-the-test-statistic\"\u003eCalculate the Test Statistic\u003c/h3\u003e\n\u003cp\u003eLet us conduct a two sample t-test with the alternative hypothesis being that the female average life expectancy is greater than that of the males\u003c/p\u003e\n\u003cp\u003eRunning the test below shoes us a p-value of less than 0.001. This tells us that the probability of obtaining a sample as extreme as the one obtained is close to zero. Therefore at a significance level of 5%, we reject the null hypothesis and state that there is strong evidence to suggest that females have a greater life expectancy that that of males.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Test alternative hypothesis\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003et.test\u003c/span\u003e(femaleExpectancy, maleExpectancy, alternative\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;g\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e##  Welch Two Sample t-test\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## data:  femaleExpectancy and maleExpectancy\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## t = 18.858, df = 182.48, p-value \u0026amp;lt; 2.2e-16\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## alternative hypothesis: true difference in means is greater than 0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## 95 percent confidence interval:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e##  4.984992      Inf\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## sample estimates:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## mean of x mean of y \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e##    80.416    74.952\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn fact, we are 95% confident that the difference between the average female life expectancy and the average male life expectancy in the United States is between 5 and 6 years. Females live on average 5-6 years longer than males in the United States.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Find confidence interval\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003et.test\u003c/span\u003e(femaleExpectancy, maleExpectancy)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e##  Welch Two Sample t-test\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## data:  femaleExpectancy and maleExpectancy\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## t = 18.858, df = 182.48, p-value \u0026amp;lt; 2.2e-16\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## alternative hypothesis: true difference in means is not equal to 0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## 95 percent confidence interval:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e##  4.892333 6.035667\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## sample estimates:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## mean of x mean of y \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e##    80.416    74.952\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"outlier-analysis\"\u003eOutlier Analysis\u003c/h3\u003e\n\u003cp\u003eWe cannot forget that we had outliers in our dataset. This might affect the results of our test. The point of outlier analysis is to see if such changes are significant.\u003c/p\u003e\n\u003cp\u003eFirst let us remove the outliers in R\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Remove outliers\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emaleExpectancy2 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e maleExpectancy[\u003cspan style=\"color:#f92672\"\u003e!\u003c/span\u003emaleExpectancy \u003cspan style=\"color:#f92672\"\u003e%in%\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eboxplot.stats\u003c/span\u003e(maleExpectancy)\u003cspan style=\"color:#f92672\"\u003e$\u003c/span\u003eout]\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen let us check the histogram and boxplots to see if the nearly normal condition is now met.\u003c/p\u003e\n\u003cp\u003eLooking at the boxplot, there are no more outliers present\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Check graphs again\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eboxplot\u003c/span\u003e(maleExpectancy2, ylab \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Age\u0026#34;\u003c/span\u003e, main \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Male Life Expectancy w/o Outliers\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/MLifeExpectBoxplotNoOutliers.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eThe histogram still appears to be unimodal and symmetric\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ehist\u003c/span\u003e(maleExpectancy2, xlab \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Age\u0026#34;\u003c/span\u003e, main \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Male Life Expectancy w/o Outliers\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/MLifeExpectHistNoOutliers.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eWithout the outliers present, the nearly normal condition is now met. We can perform the t-test.\u003c/p\u003e\n\u003cp\u003eWe can see that the hypothesis test returns the same results as before, this tells us that the outliers did not have a significant impact on our test results\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Test new alternative\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003et.test\u003c/span\u003e(femaleExpectancy, maleExpectancy2, alternative\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;g\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e##  Welch Two Sample t-test\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## data:  femaleExpectancy and maleExpectancy2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## t = 19.471, df = 184.03, p-value \u0026amp;lt; 2.2e-16\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## alternative hypothesis: true difference in means is greater than 0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## 95 percent confidence interval:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e##  5.000048      Inf\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## sample estimates:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## mean of x mean of y \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e##  80.41600  74.95204\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRedoing the confidence intervals, we can see that it did not change greatly\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Find new confidence interval\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003et.test\u003c/span\u003e(femaleExpectancy, maleExpectancy2)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e##  Welch Two Sample t-test\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## data:  femaleExpectancy and maleExpectancy2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## t = 19.471, df = 184.03, p-value \u0026amp;lt; 2.2e-16\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## alternative hypothesis: true difference in means is not equal to 0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## 95 percent confidence interval:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e##  4.910317 6.017601\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## sample estimates:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e## mean of x mean of y \u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e##  80.41600  74.95204\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eBy running the tests and checking the effects of the outliers in the dataset and seeing that the results did not change, we can safely conclude that our interpretations stated before are correct. There is enough evidence to suggest that females in the United States live on average longer than males. We are 95% confident that they live longer than males by 5 to 6 years.\u003c/p\u003e\n",
    "date_published": "2017.03.16",
    "tags": ["Statistics"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2017-03-14-monte-carlo-pi/",
    "url": "https://brandonrozek.com/blog/2017-03-14-monte-carlo-pi/",
    "title": "Approximate Pi using a Monte Carlo Simulation",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eUsing Monte Carlo methods, we can create a simulation that approximates pi. In this post, we will go over the math behind the approximation and the code.\u003c/p\u003e\n\u003ch2 id=\"the-math\"\u003eThe Math\u003c/h2\u003e\n\u003cp\u003ePi is a mathematical constant consisting of the ratio between the circumfrence of a circle and it’s diameter.\u003c/p\u003e\n\u003cp\u003eThe circumfrence of the circle is defined to be $$ C = 2\\pi r$$ while the diameter of the circle is $$d = 2r$$\u003c/p\u003e\n\u003cp\u003eTake the ratio between the two and you get $$\\frac{2\\pi r}{2r} = \\pi$$\u003c/p\u003e\n\u003cp\u003eNow let us consider the area of a circle. One can derive the area of a circle by taking the integral of the circumfrence with respect to it’s radius $$ A_{circle} = \\int{(2\\pi r) dr} = \\pi r^2 $$\u003c/p\u003e\n\u003cp\u003eLet us simplify the formula more by setting the radius equal to one. $$A_{circle} = \\pi$$\u003c/p\u003e\n\u003cp\u003eNow consider only the first quadrant of the circle. Since our circle is centered at the origin and all the points on the circumfrence is equidistant from the center, our area is now $$A_{circle} = \\frac{1}{4} \\pi$$\u003c/p\u003e\n\u003cp\u003eAnd bound the quarter-circle in a 1×1 box with an area of $$A_{square} = 1^2 = 1$$\u003c/p\u003e\n\u003cp\u003eNotice that the ratio between the circle and the square is a quarter of pi $$\\frac{A_{circle}}{A_{square}} = \\frac{\\frac{1}{4} \\pi}{1} = \\frac{1}{4} \\pi$$\u003c/p\u003e\n\u003ch2 id=\"simulation-and-statisitcs\"\u003eSimulation and Statisitcs\u003c/h2\u003e\n\u003cp\u003eThe formula for a circle centered at the origin with radius one is $$x^2 + y^2 = 1$$\u003c/p\u003e\n\u003cp\u003eLet us focus again on the first quadrent, and do a Monte Carlo simulation to find the area of the quarter-circle\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/circlefilled.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003eWe can do this by what is called the dart board method. We generate a random x and y between 0 and 1. If it satisfies the inequality $$x^2 + y^2 \\leq 1$$ then it counts as being inside the circle, if not then it lies outside the circle.\u003c/p\u003e\n\u003cp\u003eThat point will count as an really small area. The point will always be inside the square but may sometimes be inside the circle. Running the simulations a large number of times allows us to add up all the tiny little areas that make up the circle and the square.\u003c/p\u003e\n\u003cp\u003eTo add up these small areas we need to make an assumption. The assumption is that the variance of all the little Monte Carlo trials are the same. Since we are using a psuedo-random number generator, it is safe to assume it is met.\u003c/p\u003e\n\u003cp\u003eThis will allow us to perform a pooled empiricle probability on the simulations to sum up the areas.\u003c/p\u003e\n\u003cp\u003eMeaning the area of the circle will be the number of times that the inequality was satisfied $$A_{circle} = \\# Successes$$\u003c/p\u003e\n\u003cp\u003eAnd the area of the square will be the number of times the simulation was run, since the random numbers generated will always be between 0 and 1 $A_{square} = \\# Trials$\u003c/p\u003e\n\u003cp\u003eRecall that taking the ratio of the area of the circle and the area of the square is a fourth of pi. $$\\frac{\\frac{1}{4} \\pi}{1} = \\frac{1}{4} \\pi$$\u003c/p\u003e\n\u003cp\u003eMultiply this number by 4 and you get the value for pi.\u003c/p\u003e\n\u003cp\u003eThis tells us that four times the probability that the randomly generated point is in the circle is equal to pi.\u003c/p\u003e\n\u003cp\u003e$$\\pi = 4 * (Probability\\ of\\ being\\ inside\\ circle) = 4 * \\frac{\\# Success}{\\# Trials} = 4 * \\frac{A_{circle}}{A_{square}}$$\u003c/p\u003e\n\u003ch2 id=\"implementation\"\u003eImplementation\u003c/h2\u003e\n\u003cp\u003eFor the Monte Carlo simulation I used Java. The BigDecimal implementation was used so that there wouldn’t be any issue with integer size limits\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/** Calculates Pi\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e  * @author Brandon Rozek\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// Big Integers are used so we don\u0026#39;t run into the integer size limit\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e java.math.BigInteger;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e java.math.BigDecimal;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eMonteCarloPi\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estatic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e(String\u003cspan style=\"color:#f92672\"\u003e[]\u003c/span\u003e args) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                BigInteger successes \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e BigInteger.\u003cspan style=\"color:#a6e22e\"\u003eZERO\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                BigInteger trials \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e BigInteger.\u003cspan style=\"color:#a6e22e\"\u003eZERO\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor this simulation, we will run 1,000,000,000 trials\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eBigInteger numTrials \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e BigInteger(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;1000000000\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/*\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e    Monte Carlo Simulation\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e        Generate a random point 0 \u0026amp;lt;= x \u0026amp;lt; 1 and 0 \u0026amp;lt;= y \u0026amp;lt; 1\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e        If the generated point satisfies x^2 + x^2 \u0026amp;lt; 1\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e           Count as a success\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e        Keep track of the number of trials and successes\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (; trials.\u003cspan style=\"color:#a6e22e\"\u003ecompareTo\u003c/span\u003e(numTrials) \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003elt; 0; trials \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e trials.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(BigInteger.\u003cspan style=\"color:#a6e22e\"\u003eONE\u003c/span\u003e)) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edouble\u003c/span\u003e randomX \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Math.\u003cspan style=\"color:#a6e22e\"\u003erandom\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003edouble\u003c/span\u003e randomY \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Math.\u003cspan style=\"color:#a6e22e\"\u003erandom\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (Math.\u003cspan style=\"color:#a6e22e\"\u003epow\u003c/span\u003e(randomX, 2) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e Math.\u003cspan style=\"color:#a6e22e\"\u003epow\u003c/span\u003e(randomY, 2) \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003elt; 1) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        successes \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e successes.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(BigInteger.\u003cspan style=\"color:#a6e22e\"\u003eONE\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd then we finalize it with a quick calculation of pi\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// (Number of successes) / (Number of trials) * 4 gives the approximation for pi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eBigDecimal pi \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e BigDecimal(successes)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                       .\u003cspan style=\"color:#a6e22e\"\u003edivide\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e BigDecimal(trials))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                       .\u003cspan style=\"color:#a6e22e\"\u003emultiply\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e BigDecimal(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;4\u0026#34;\u003c/span\u003e));\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSystem.\u003cspan style=\"color:#a6e22e\"\u003eout\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eprintln\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;The calculated value of pi is: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e pi);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eWe found an approximation of pi using the Monte Carlo methods! I find that really awesome, however, there are some concerns I have with this approach.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003eWe don’t keep track of double counting. One possible solution for this is increasing the radius and bounding box appropriately so that the probability of double counting is low.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eSpeed. The more trials you ask it to run, the longer it takes to perform all of the simulations. One possible way around this is to write a parrallel version of this code. That’s possible because of the equal variance that we spoke of earlier. Pooling the successses and trials will still result in a good approximation.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e",
    "date_published": "2017.03.14",
    "tags": ["Math","Java"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2017-03-09-simplifying-expressions-octave/",
    "url": "https://brandonrozek.com/blog/2017-03-09-simplifying-expressions-octave/",
    "title": "Simplifying Expressions with Octave",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eOctave is a high level programming language intended for numerical computations. One of the cool features of this is that with symbolic expressions, you can then simplify mathematical expressions.\u003c/p\u003e\n\u003ch2 id=\"setup\"\u003eSetup\u003c/h2\u003e\n\u003cp\u003eFirst install \u003ca href=\"https://www.gnu.org/software/octave/\"\u003eOctave\u003c/a\u003e and the \u003ca href=\"https://octave.sourceforge.io/symbolic/\"\u003esymbolic package\u003c/a\u003e using the website or your package manager of choice.\u003c/p\u003e\n\u003cp\u003eThen in octave type in the following code\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-MATLAB\" data-lang=\"MATLAB\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epkg load symbolic\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"usage\"\u003eUsage\u003c/h2\u003e\n\u003cp\u003eFor every variable not defined earlier in your expression, make sure to declare it as a symbolic data type\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-MATLAB\" data-lang=\"MATLAB\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esyms x y\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThen make an expression\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-MATLAB\" data-lang=\"MATLAB\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eexpr = y \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e sin(x)^\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e cos(x)^\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can then ask Octave to simplify the expression for you\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-MATLAB\" data-lang=\"MATLAB\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esimp_expr = simplify(expr)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDisplaying it shows it as\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-MATLAB\" data-lang=\"MATLAB\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e(sym) y \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhich is indeed a simplification using a trig identity 🙂\u003c/p\u003e\n\u003cp\u003eUpdate: Octave\u0026rsquo;s symbolic is based on \u003ca href=\"https://www.sympy.org/en/index.html\"\u003eSymPy\u003c/a\u003e. If you\u0026rsquo;re confortable with Python, I recommend checking it out.\u003c/p\u003e",
    "date_published": "2017.03.09",
    "tags": null,
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2017-03-07-uniformity-math-random/",
    "url": "https://brandonrozek.com/blog/2017-03-07-uniformity-math-random/",
    "title": "Uniformity of Math.random()",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThere are many cases where websites use random number generators to influence some sort of page behavior. One test to ensure the quality of a random number generator is to see if after many cases, the numbers produced follow a uniform distribution.\u003c/p\u003e\n\u003cp\u003eToday, I will compare Internet Explorer 11, Chrome, and Firefox on a Windows 7 machine and report my results.\u003c/p\u003e\n\u003ch2 id=\"hypothesis\"\u003eHypothesis\u003c/h2\u003e\n\u003cp\u003eH0: The random numbers outputted follow the uniform distribution\u003c/p\u003e\n\u003cp\u003eHA: The random numbers outputted do not follow the uniform distribution\u003c/p\u003e\n\u003ch2 id=\"gathering-data\"\u003eGathering Data\u003c/h2\u003e\n\u003cp\u003eI wrote a small \u003ca href=\"http://share.zeropointshift.com/files/2017/03/random.html\"\u003ewebsite\u003c/a\u003e and obtained my data by getting the CSV outputted when I use IE11, Firefox, and Chrome.\u003c/p\u003e\n\u003cp\u003eThe website works by producing a random number using \u003ccode\u003eMath.random()\u003c/code\u003e between 1 and 1000 inclusive and calls the function 1,000,000 times. Storing its results in a file\u003c/p\u003e\n\u003cp\u003eThis website produces a file with all the numbers separated by a comma. We want these commas to be replaced by newlines. To do so, we can run a simple command in the terminal\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egrep -oE \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;[0-9]+\u0026#39;\u003c/span\u003e Random.csv \u0026amp;gt; Random_corrected.csv\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDo this with all three files and make sure to keep track of which is which.\u003c/p\u003e\n\u003cp\u003eHere are a copy of my files for \u003ca href=\"/data/browser-random/Firefox_corrected.csv\"\u003eFirefox\u003c/a\u003e, \u003ca href=\"/data/browser-random/Chrome_corrected-1.csv\"\u003eChrome\u003c/a\u003e, and \u003ca href=\"/data/browser-random/IE11_corrected.csv\"\u003eIE11\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"check-conditions\"\u003eCheck Conditions\u003c/h2\u003e\n\u003cp\u003eSince we\u0026rsquo;re interested in if the random values occur uniformly, we need to perform a Chi-Square test for Goodness of Fit. With every test comes some assumptions\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eCounted Data Condition:\u003c/strong\u003e The data can be converted from quantatative to count data.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eIndependence Assumption:\u003c/strong\u003e One random value does not affect another.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eExpected Cell Frequency Condition:\u003c/strong\u003e The expected counts are going to be 10000\u003c/p\u003e\n\u003cp\u003eSince all of the conditions are met, we can use the Chi-square test of Goodness of Fit\u003c/p\u003e\n\u003ch2 id=\"descriptive-statistics\"\u003eDescriptive Statistics\u003c/h2\u003e\n\u003cp\u003eFor the rest of the article, we will use R for analysis. Looking at the histograms for the three browsers below. The random numbers all appear to occur uniformly\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003erm\u003c/span\u003e(list\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003els\u003c/span\u003e())\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echrome \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eread.csv\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;~/Chrome_corrected.csv\u0026#34;\u003c/span\u003e, header \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e F)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efirefox \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eread.csv\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;~/Firefox_corrected.csv\u0026#34;\u003c/span\u003e, header \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e F)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eie11 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eread.csv\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;~/IE11_corrected.csv\u0026#34;\u003c/span\u003e, header \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e F)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ehist\u003c/span\u003e(ie11\u003cspan style=\"color:#f92672\"\u003e$\u003c/span\u003eV1, main \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Distribution of Random Values for IE11\u0026#34;\u003c/span\u003e, xlab \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Random Value\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/ie11hist.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ehist\u003c/span\u003e(firefox\u003cspan style=\"color:#f92672\"\u003e$\u003c/span\u003eV1, main \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Distribution of Random Values for Firefox\u0026#34;\u003c/span\u003e, xlab \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Random Value\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/firefoxhist.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ehist\u003c/span\u003e(chrome\u003cspan style=\"color:#f92672\"\u003e$\u003c/span\u003eV1, main \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Distribution of Random Values for Chrome\u0026#34;\u003c/span\u003e, xlab \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Random Value\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg src=\"/files/images/blog/chromehist.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003ch2 id=\"chi-square-test\"\u003eChi-Square Test\u003c/h2\u003e\n\u003cp\u003eBefore we run our test, we need to convert the quantatative data to count data by using the plyr package\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#Transform to count data\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003elibrary\u003c/span\u003e(plyr)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echrome_count \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecount\u003c/span\u003e(chrome)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efirefox_count \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecount\u003c/span\u003e(firefox)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eie11_count \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecount\u003c/span\u003e(ie11)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eRun the tests\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-R\" data-lang=\"R\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Chi-Square Test for Goodness-of-Fit\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echrome_test \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003echisq.test\u003c/span\u003e(chrome_count\u003cspan style=\"color:#f92672\"\u003e$\u003c/span\u003efreq)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003efirefox_test \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003echisq.test\u003c/span\u003e(firefox_count\u003cspan style=\"color:#f92672\"\u003e$\u003c/span\u003efreq)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eie11_test \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003echisq.test\u003c/span\u003e(ie11_count\u003cspan style=\"color:#f92672\"\u003e$\u003c/span\u003efreq)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Test results\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echrome_test\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAs you can see in the test results below, we fail to reject the null hypothesis at a 5% significance level because all of the p-values are above 0.05.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e## \r\n##  Chi-squared test for given probabilities\r\n## \r\n## data:  chrome_count$freq\r\n## X-squared = 101.67, df = 99, p-value = 0.4069\r\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003efirefox_test\u003c/code\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e## \r\n##  Chi-squared test for given probabilities\r\n## \r\n## data:  firefox_count$freq\r\n## X-squared = 105.15, df = 99, p-value = 0.3172\r\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003eie11_test\u003c/code\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e## \r\n##  Chi-squared test for given probabilities\r\n## \r\n## data:  ie11_count$freq\r\n## X-squared = 78.285, df = 99, p-value = 0.9384\r\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eAt a 5% significance level, we fail to obtain enough evidence to suggest that the distribution of random number is not uniform. This is a good thing since it shows us that our random number generators give all numbers an equal chance of being represented. We can use \u003ccode\u003eMath.random()\u003c/code\u003e with ease of mind.\u003c/p\u003e",
    "date_published": "2017.03.07",
    "tags": ["Statistics"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2017-03-07-knit-document-rstudio/",
    "url": "https://brandonrozek.com/blog/2017-03-07-knit-document-rstudio/",
    "title": "Knit a Document in RStudio",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eIn case you were wondering how I got all the R code and output for the \u003ca href=\"https://brandonrozek.com/portfolio/male-vs-female-life-expectancy/\"\u003e“Do females live longer than males?”\u003c/a\u003e page. There is actually a function in RStudio that allows one to compile a report based on code and it’s output.\u003c/p\u003e\n\u003cp\u003eFirst go to File-\u0026gt;Knit Document. If this is your first time, then it will install RMarkdown, a dependency this tool needs to compile the report.\u003c/p\u003e\n\u003cp\u003eOnce that is downloaded, it will let you choose between three different file formats (HTML, PDF, MS Word). For the purposes of blog posts, I like to output it in HTML so I can copy and paste the code. But for personal use, I like using PDFs\u003c/p\u003e\n\u003cp\u003eAfter you select the file format, hit compile, and voila! A nice neat compiled report is created for you. Here is a \u003ca href=\"/files/reports/LifeExpectancy.pdf\"\u003epdf\u003c/a\u003e example of the report I made.\u003c/p\u003e",
    "date_published": "2017.03.07",
    "tags": ["R"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2016-08-16-pass-password-manager/",
    "url": "https://brandonrozek.com/blog/2016-08-16-pass-password-manager/",
    "title": "Pass the password manager",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLooking over the list of programs installed on my computer, one of my all time favorites is a program called \u003ca href=\"https://www.passwordstore.org/\"\u003ePass\u003c/a\u003e. It’s a program available under most Linux distributions, Mac OS X (through \u003ca href=\"http://brew.sh/\"\u003eBrew\u003c/a\u003e), \u003ca href=\"https://github.com/mbos/Pass4Win\"\u003eWindows\u003c/a\u003e, \u003ca href=\"https://play.google.com/store/apps/details?id=com.zeapo.pwdstore\"\u003eAndroid\u003c/a\u003e, and \u003ca href=\"https://github.com/davidjb/pass-ios#readme\"\u003eiOS\u003c/a\u003e. It stores all of the passwords using gpg encryption and stores them as actual files on the hard disk. Meaning if you wanted, you can sync them to all your devices!\u003c/p\u003e\n\u003cp\u003eThis program assumes a light familiarity with the terminal\u003c/p\u003e\n\u003ch2 id=\"setup\"\u003eSetup\u003c/h2\u003e\n\u003cp\u003eFirst if you do not already have a gpg key, \u003ca href=\"http://wooledge.org/~greg/crypto/node41.html\"\u003ecreate one\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThen in the terminal type in\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003epass init youremail@address\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eSubstituting youremail@address with the email associated with your gpg key.\u003c/p\u003e\n\u003cp\u003eThis will autmatically create an empty repository under your home folder in a folder labeled \u003ccode\u003e.pass\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eIf you are switching from an existing password manager, check to see if on the \u003ca href=\"https://www.passwordstore.org/\"\u003ePass homepage\u003c/a\u003e, there doesn’t exist a script to help you out\u003c/p\u003e\n\u003ch2 id=\"basic-tasks\"\u003eBasic Tasks\u003c/h2\u003e\n\u003cp\u003eTo insert a password into pass\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003epass insert password-name\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eIt will then prompt you to enter the password\u003c/p\u003e\n\u003cp\u003eTo show passwords you have already inserted\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003epass\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eTo show an individual password\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003epass password-name\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eBut generally I find it handy to have it automatically in my clipboard. You can do that with the -c option\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003epass -c password-name\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eYou can generate new secure passwords using pass. (-c copies the result into your clipboard)\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003epass generate -c password-name password-length\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eIf you don’t want it to output symbols, use the -n option to restrict it to alphanumericals\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003epass generate -n -c password-name password-length\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eAnother command i find handy is the \u003ccode\u003efind\u003c/code\u003e command. Mainly because I have over a 100 passwords in this system and i tend to forget what I named some of them\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003epass find search-string\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThere are too many commands to list them all, but if you ever want to find out more, check out the manual entry page for \u003ccode\u003epass\u003c/code\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eman pass\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"sync\"\u003eExtra: Syncing\u003c/h2\u003e\n\u003cp\u003eI use a nextCloud instance on my server to sync my passwords, but I don’t see a reason why this wouldn’t work with other projects like dropbox, syncthing, or any other sync solution\u003c/p\u003e\n\u003cp\u003eSome sync solutions don’t like to sync folders that begin with a ‘.’, my solution around this is to create a symbolic link between that and a folder you wish to link it to\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eln -s /path/to/home/folder/.password-store /path/to/sync/folder/password-store\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThen you just need to make sure to make the same link to all your other computers\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eI like Pass for it’s ease of use and for the fact that I’m not tied into any one company for managing my passwords. It’s based on open source tools and the fact I didn’t have to configure a database is a huge plus for me\u003c/p\u003e\n\u003cp\u003eIf you’re in a need of a password manager (I hope you have more than one password), then give pass a shot. It served me and my many passwords well.\u003c/p\u003e",
    "date_published": "2016.08.16",
    "tags": ["Security"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2015-12-27-creating-vcards-from-h-cards/",
    "url": "https://brandonrozek.com/blog/2015-12-27-creating-vcards-from-h-cards/",
    "title": "Creating vCards from h-cards",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eMicroformats is semantic HTML used to convey metadata. Using an userscript, I can generate a vCard from the representative h-card of the page. The code for this is on this gist \u003ca href=\"https://gist.github.com/brandonrozek/e0153b2733e947fa9c87\"\u003ehere.\u003c/a\u003e\u003c/p\u003e\n\u003ch3 id=\"terminology\"\u003eTerminology\u003c/h3\u003e\n\u003cp\u003e\u003ca href=\"http://microformats.org/wiki/Main_Page\"\u003eMicroformats\u003c/a\u003e allows search engines, browsers, websites, or people like me to consume content on a site.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"http://microformats.org/wiki/h-card\"\u003eH-card\u003c/a\u003e is a type of microformat that serves as a contact card for people and organizations.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://en.wikipedia.org/wiki/VCard\"\u003evCard\u003c/a\u003e is the standard for electronic business cards. They\u0026rsquo;re most likely used in your phone to store contacts.\u003c/p\u003e\n\u003cp\u003eUserscript is essentially JavaScript that runs in the \u003ca href=\"http://www.greasespot.net/\"\u003eGreasemonkey\u003c/a\u003e extension.\u003c/p\u003e\n\u003ch3 id=\"what-ill-need\"\u003eWhat I\u0026rsquo;ll need\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eMicroformat parser\u003c/li\u003e\n\u003cli\u003eWay to find the representative h-card\u003c/li\u003e\n\u003cli\u003eWay to generate a vCard from an h-card\u003c/li\u003e\n\u003cli\u003eThe userscript itself\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"implementation\"\u003eImplementation\u003c/h3\u003e\n\u003cp\u003eTo keep everything in small \u003ca href=\"https://en.wikipedia.org/wiki/Modular_programming\"\u003ereusable components\u003c/a\u003e, I created four different sections. Thankfully, \u003ca href=\"http://glennjones.net/\"\u003eGlenn Jones\u003c/a\u003e already wrote a JavaScript microformats parser called \u003ca href=\"https://github.com/glennjones/microformat-shiv\"\u003emicroformat-shiv.\u003c/a\u003e It’s licensed with \u003ca href=\"https://tldrlegal.com/license/mit-license\"\u003eMIT\u003c/a\u003e, so we can use it, yay!\u003c/p\u003e\n\u003cp\u003eNext, I need to find the representative h-card of the page. Following the \u003ca href=\"http://microformats.org/wiki/representative-h-card-parsing\"\u003einstructions\u003c/a\u003e on the microformats wiki, I wrote the following code.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/*\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e   representative-h-card - v0.1.0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e   Copyright (c) 2015 Brandon Rozek\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e   Licensed MIT \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/**\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\tFinds the representative h-card of the page\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\t[http://microformats.org/wiki/representative-h-card-parsing]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\t@returns representative h-card if found, null otherwise\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e**/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erepresentativeHCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ehCards\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ehCards\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t} \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ehCards\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eurlsMatchURL\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ehCards\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e], \u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e)) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCards\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#a6e22e\"\u003ehCards\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e]];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t} \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCards\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eurlsMatchURL\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ehCards\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e], \u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003euidsMatchURL\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ehCards\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e], \u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erelMeMatchURL\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ehCards\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e))) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCards\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#a6e22e\"\u003ehCards\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e]];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eurlsMatchURL\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eurls\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eproperties\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003etypeof\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eurls\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;object\u0026#34;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eurls\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eURL\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eurls\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e]).\u003cspan style=\"color:#a6e22e\"\u003etoString\u003c/span\u003e() \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eURL\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003etoString\u003c/span\u003e()) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003euidsMatchURL\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003euids\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eproperties\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003euid\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003etypeof\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003euids\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;object\u0026#34;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003euids\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eURL\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003euids\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e]).\u003cspan style=\"color:#a6e22e\"\u003etoString\u003c/span\u003e() \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eURL\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003etoString\u003c/span\u003e()) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e};\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erelMeMatchURL\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003emicroformats\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eme\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emicroformats\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erels\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eme\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003etypeof\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eme\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;object\u0026#34;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eme\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eURL\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eme\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e]).\u003cspan style=\"color:#a6e22e\"\u003etoString\u003c/span\u003e() \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eURL\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003etoString\u003c/span\u003e()) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNext up, is making the vCard. For this, I had to look at the \u003ca href=\"https://tools.ietf.org/html/rfc6350\"\u003evCard 4.0 specification\u003c/a\u003e to figure out what the property names and values are. Then I browsed around different \u003ca href=\"http://indieweb.thatmustbe.us/\"\u003esites\u003c/a\u003e (takes you to a random \u003ca href=\"https://indiewebcamp.com/\"\u003eIndieweb\u003c/a\u003e site)  to figure out which properties are the most common.\u003c/p\u003e\n\u003cp\u003eThe properties I ended up adding to the vCard.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003ename\u003c/li\u003e\n\u003cli\u003ephoto\u003c/li\u003e\n\u003cli\u003etelephone numbers\u003c/li\u003e\n\u003cli\u003eURLs\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://en.wikipedia.org/wiki/Instant_Messaging_and_Presence_Protocol\"\u003eIMPPs\u003c/a\u003e (Instant Messaging and Presence Protocol)\u003c/li\u003e\n\u003cli\u003eemails\u003c/li\u003e\n\u003cli\u003eroles\u003c/li\u003e\n\u003cli\u003ecategories\u003c/li\u003e\n\u003cli\u003enotes\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e As I was browsing around, I noticed that a few people would have empty values for certain properties on their h-card. To avoid having this show up on the vCard, I added a filter that takes out empty strings.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/*\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e   vCard-from-h-card - v0.1.0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e   Copyright (c) 2015 Brandon Rozek\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e   Licensed MIT \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emakeVCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003evCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;BEGIN:VCARDnVERSION:4.0n\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e//Add full name\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e].\u003cspan style=\"color:#a6e22e\"\u003eproperties\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003etypeof\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;object\u0026#34;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eremoveEmptyStrings\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003elt\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#a6e22e\"\u003evCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;FN: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;n\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e//Add photo\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ephoto\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e].\u003cspan style=\"color:#a6e22e\"\u003eproperties\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ephoto\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003etypeof\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ephoto\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;object\u0026#34;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003ephoto\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eremoveEmptyStrings\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003elt\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ephoto\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#a6e22e\"\u003evCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;PHOTO: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ephoto\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;n\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e//Add phone number\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etel\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e].\u003cspan style=\"color:#a6e22e\"\u003eproperties\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003etel\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003etypeof\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003etel\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;object\u0026#34;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003etel\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eremoveEmptyStrings\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003elt\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003etel\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003etry\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eURL\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003etel\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e]).\u003cspan style=\"color:#a6e22e\"\u003eschema\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;sms:\u0026#34;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\t\u003cspan style=\"color:#a6e22e\"\u003evCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;TEL;TYPE=text;VALUE=text: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eURL\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003etel\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e]).\u003cspan style=\"color:#a6e22e\"\u003epathname\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;n\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t} \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\t\u003cspan style=\"color:#a6e22e\"\u003evCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;TEL;TYPE=voice;VALUE=text: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eURL\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003etel\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e]).\u003cspan style=\"color:#a6e22e\"\u003epathname\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;n\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t} \u003cspan style=\"color:#66d9ef\"\u003ecatch\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ee\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#a6e22e\"\u003evCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;TEL;TYPE=voice;VALUE=text: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etel\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;n\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e//Add URLs\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e].\u003cspan style=\"color:#a6e22e\"\u003eproperties\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003etypeof\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;object\u0026#34;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eremoveEmptyStrings\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003elt\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#a6e22e\"\u003evCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;URL: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;n\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eimpp\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e].\u003cspan style=\"color:#a6e22e\"\u003eproperties\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eimpp\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e//Add IMPP (Instant Messaging and Presence Protocol)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003etypeof\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eimpp\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;object\u0026#34;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003eimpp\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eremoveEmptyStrings\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eimpp\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#a6e22e\"\u003evCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;IMPP;PREF=\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eimpp\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;n\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e//Add emails\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eemail\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e].\u003cspan style=\"color:#a6e22e\"\u003eproperties\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eemail\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003etypeof\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eemail\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;object\u0026#34;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003eemail\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eremoveEmptyStrings\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eemail\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003etry\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#a6e22e\"\u003evCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;EMAIL: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eURL\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eemail\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e]).\u003cspan style=\"color:#a6e22e\"\u003epathname\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;n\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t} \u003cspan style=\"color:#66d9ef\"\u003ecatch\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ee\u003c/span\u003e) { \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#a6e22e\"\u003evCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;EMAIL: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eemail\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;n\u0026#34;\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t}\t\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e//Add roles\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erole\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e].\u003cspan style=\"color:#a6e22e\"\u003eproperties\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erole\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003etypeof\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003erole\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;object\u0026#34;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003erole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eremoveEmptyStrings\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003elt\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003erole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#a6e22e\"\u003evCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ROLE: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erole\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;n\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e//Add Organizations\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eorg\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e].\u003cspan style=\"color:#a6e22e\"\u003eproperties\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eorg\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003etypeof\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eorg\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;object\u0026#34;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003eorg\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eremoveEmptyStrings\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003elt\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003eorg\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#a6e22e\"\u003evCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ORG: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eorg\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;n\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e//Add Categories\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecategory\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e].\u003cspan style=\"color:#a6e22e\"\u003eproperties\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ecategory\u003c/span\u003e; \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003etypeof\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecategory\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;object\u0026#34;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003evCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;CATEGORIES: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecategory\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eremoveEmptyStrings\u003c/span\u003e().\u003cspan style=\"color:#a6e22e\"\u003ejoin\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;,\u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;n\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e//Add notes\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003enote\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCard\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e].\u003cspan style=\"color:#a6e22e\"\u003eproperties\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003enote\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003etypeof\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003enote\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;object\u0026#34;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003enote\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eremoveEmptyStrings\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003elt\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003enote\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#a6e22e\"\u003evCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;NOTE: \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003enote\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;n\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003evCard\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;END:VCARD\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eArray.\u003cspan style=\"color:#a6e22e\"\u003eprototype\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eremoveEmptyStrings\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003efilter\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e) { \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e!==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow for the final part, making the userscript. Inspired by \u003ca href=\"https://snarfed.org/\"\u003eRyan Barret\u003c/a\u003e and his userscript \u003ca href=\"https://github.com/snarfed/misc/blob/master/userscripts/lets_talk.user.js\"\u003eLet\u0026rsquo;s Talk,\u003c/a\u003e this userscript brings all of the above modules together. First it grabs the microformats from the page using microformat-shiv.\u003c/p\u003e\n\u003cp\u003eFor some reason, when I tried filtering it by ‘h-card’ it froze my computer. So I wrote my own little filter instead.\u003c/p\u003e\n\u003cp\u003eAfter I grab the representative h-card from the page using the little module I wrote, I generated a vCard. With the vCard generated, I set up a little HTML and CSS to display the link in the top left corner of the screen.\u003c/p\u003e\n\u003cp\u003eThe link is actually a \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs\"\u003edata uri\u003c/a\u003e that has all of the information of the vCard encoded in it. Depending on your browser, once you click the link you might have to hit CTRL-S to save.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/*\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e   show-vCard - v0.1.0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e   Copyright (c) 2015 Brandon Rozek\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e   Licensed MIT \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efilterMicroformats\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003efilter\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003enewItems\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ek\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ek\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e].\u003cspan style=\"color:#a6e22e\"\u003etype\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ek\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003efilter\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e].\u003cspan style=\"color:#a6e22e\"\u003etype\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ek\u003c/span\u003e]) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#a6e22e\"\u003enewItems\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003epush\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003enewItems\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erender\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehCards\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efilterMicroformats\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eMicroformats\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(), [\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;h-card\u0026#39;\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eperson\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erepresentativeHCard\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ehCards\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003elocation\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eorigin\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eperson\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003enode\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e document.\u003cspan style=\"color:#a6e22e\"\u003ecreateElement\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;div\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003enode\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003esetAttribute\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;class\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;lt\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003elink\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026amp;lt;a href=\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003etext\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003evcf\u003c/span\u003e;\u003cspan style=\"color:#a6e22e\"\u003ebase64\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003e\u0026#34; + btoa(makeVCard(person))+ \u0026#34;\u0026#34; target=\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e_blank\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026gt;vCard\u0026amp;lt;/a\u0026gt;\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003estyle\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t.lt { \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t\tposition: absolute; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t\tleft: 24px; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t\ttop: 0; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t\tcolor: #DDD; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t\tbackground-color: #FFD700; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t\tz-index: 9999; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t\tborder-width: medium 1px 1px; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t\tborder-style: none solid solid; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t\tborder-color: #DDD #C7A900 #9E8600; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t\tbox-shadow: 0px 1px rgba(0, 0, 0, 0.1), 0px 1px 2px rgba(0, 0, 0, 0.1), 0px 1px rgba(255, 255, 255, 0.34) inset; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t\tborder-radius: 0px 0px 4px 4px; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t        } \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t.lt a { \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t\tpadding: .5rem; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t \tcolor: #8f6900; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t \ttext-shadow: 0px 1px #FFE770; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t\tborder: medium none; \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t\t} \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\t\t     \u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003enode\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003einnerHTML\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003elink\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003estyle\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tdocument.\u003cspan style=\"color:#a6e22e\"\u003ebody\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eappendChild\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003enode\u003c/span\u003e);\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocument.\u003cspan style=\"color:#a6e22e\"\u003eaddEventListener\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;DOMContentLoaded\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003erender\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSadly, I have no way of controlling the file name when you save it so you\u0026rsquo;ll have to manually rename it to something more meaningful than a random string of characters. Also remember to add the extension ‘.vcf’ for it to be recognized by some devices.\u003c/p\u003e\n\u003ch3 id=\"conclusion\"\u003eConclusion\u003c/h3\u003e\n\u003cp\u003eFire up your favorite userscript handling tool and add the \u003ca href=\"https://gist.github.com/brandonrozek/e0153b2733e947fa9c87\"\u003escript\u003c/a\u003e in! Of course, since it\u0026rsquo;s pure JavaScript, you can also add it to your own site to serve the same purpose.\u003c/p\u003e\n\u003cp\u003eI ran into a small problem loading a contact onto my Android 5.0.2 phone. Apparently, they don\u0026rsquo;t support vCard 4.0 yet so I had to go into the file and change the line that says “VERSION 4.0” to “VERSION 3.0” which then allowed me to import the file into my contacts.\u003c/p\u003e\n\u003cp\u003eAs with all the code I write, feel free to comment/criticize. I love hearing feedback so if you spot anything, \u003ca href=\"mailto:brozek@brandonrozek.com\"\u003econtact me\u003c/a\u003e 🙂\u003c/p\u003e\n\u003cp\u003eAlso posted on \u003ca href=\"http://news.indiewebcamp.com/en\"\u003eIndieNews\u003c/a\u003e{.u-syndication}\u003c/p\u003e",
    "date_published": "2015.12.27",
    "tags": ["Web"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2015-12-22-playing-with-qr-codes/",
    "url": "https://brandonrozek.com/blog/2015-12-22-playing-with-qr-codes/",
    "title": "Playing with QR Codes",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLooking at Aaron Parecki\u0026rsquo;s \u003ca href=\"https://aaronparecki.com/articles/2015/10/05/1/fun-with-qr-codes\"\u003e“Fun with QR Codes”\u003c/a\u003e inspired me to play with QR Codes myself. Using the tool \u003ca href=\"http://research.swtch.com/qr/draw\"\u003eQArt Coder\u003c/a\u003e made by \u003ca href=\"https://plus.google.com/116810148281701144465\"\u003eRuss Cox\u003c/a\u003e, I made a QR Code with my face in it!\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/qrcode-large-1.png\" alt=\"\"\u003e\u003c/p\u003e\n\u003ch3 id=\"why-qr-codes\"\u003eWhy QR Codes?\u003c/h3\u003e\n\u003cp\u003eQR Codes aren\u0026rsquo;t prevalent in the United States of America. In fact, they\u0026rsquo;re often viewed by technologists as a \u003ca href=\"http://picturesofpeoplescanningqrcodes.tumblr.com/\"\u003ejoke\u003c/a\u003e. But as \u003ca href=\"http://www.christinaxu.org/\"\u003eChristina Xu\u003c/a\u003e points out on her Medium post \u003ca href=\"https://medium.com/chrysaora-weekly/pictures-of-chinese-people-scanning-qr-codes-a564047ec58f\"\u003e“Pictures of Chinese People Scanning QR Codes”\u003c/a\u003e, it\u0026rsquo;s not a joke everywhere. In fact, many people actually depend on this humble little QR code to get by in their day. Another story, my family and I go out to eat sometimes. Occasionally, we\u0026rsquo;ll knock into a Heinz Ketchup bottle.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/files/images/blog/heinz-2-768x1024.jpg\" alt=\"Picture of Heinz ketch bottle with QR Code\"\u003e\u003c/p\u003e\n\u003cp\u003eImage by Azad Zahoory.\u003c/p\u003e\n\u003cp\u003eMy brother would get so excited whenever he saw this. This little ketchup bottle meant that we got to play a \u003ca href=\"http://www.heinztablegames.com/game_selector.html\"\u003etrivia game\u003c/a\u003e. No matter what you think of QR Codes, vanity or not, there are people who use it every day. So have some fun and make yourself a QR Code of your own. You know, just in case 😉\u003c/p\u003e\n\u003ch3 id=\"setting-up-the-qr-code\"\u003eSetting up the QR Code\u003c/h3\u003e\n\u003cp\u003eFirst, you need a picture. I used a picture of myself. Then for the QArt Coder site to work correctly, the image needs to be in high-contrast. Meaning, it needs to be mostly black and white. To accomplish this, fire up \u003ca href=\"https://www.gimp.org/\"\u003eGimp\u003c/a\u003e (or your image editor of choice), and hit Color -\u0026gt; Threshold. That will give you a black and white version of the picture where you can then play around with the slider to get it just how you want it. After that, open up \u003ca href=\"http://research.swtch.com/qr/draw\"\u003eQArt Coder\u003c/a\u003e and upload the image onto the site. Once that\u0026rsquo;s accomplished, you might need to play with the QR Size, Image Size, Rotation, and other options. After that, hit “Save this QR Code” and viola! You got yourself three different sizes of QR codes to use. What should you do with the QR Code after you get it? First, brag to your friends about how cool it looks. Then maybe put it on your \u003ca href=\"https://www.webdesignerdepot.com/2011/07/30-creative-qr-code-business-cards/\"\u003ebusiness card\u003c/a\u003e. If you can think of any other \u003cdel\u003eexcuses\u003c/del\u003e uses of QR Codes \u003ca href=\"mailto:brozek@brandonrozek.com\"\u003econtact me\u003c/a\u003e 🙂\u003c/p\u003e\n",
    "date_published": "2015.12.22",
    "tags": null,
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2015-11-30-limiting-cache-service-workers-revisited3/",
    "url": "https://brandonrozek.com/blog/2015-11-30-limiting-cache-service-workers-revisited3/",
    "title": "Limiting the Cache in Service Workers Revisited",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eSummary: I rewrote how cache limiting works to address a few problems listed later in this post. Check out the \u003ca href=\"https://gist.github.com/brandonrozek/0cf038df40a913fda655\"\u003egist\u003c/a\u003e for the updated code.\u003c/p\u003e\n\u003cp\u003eI wrote a function in my \u003ca href=\"https://brandonrozek.com/2015/11/service-workers/\"\u003eprevious service worker post\u003c/a\u003e to help limit the cache. Here’s a reminder of what it looked like.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003elimitCache\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003emaxItems\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e().\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emaxItems\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#66d9ef\"\u003edelete\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t})\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"the-problem\"\u003eThe Problem\u003c/h3\u003e\n\u003cp\u003eJeremy Keith updated the service worker on his site and noticed that the images has blown past the amount he allocated for it (\u003ca href=\"https://adactio.com/journal/9844\"\u003epost\u003c/a\u003e). Looking back at my service worker, I noticed that mine has the same shortcoming as well. So what happened? Service workers function in an asynchronous manner. Meaning it can be processing not just one, but many fetch events at the same time. This comes into conflict when there are synchronous instructions such as deleting the first item from the cache which Jeremy describes in his follow up \u003ca href=\"https://adactio.com/journal/9888\"\u003epost\u003c/a\u003e.\u003c/p\u003e\n\u003ch3 id=\"a-solution\"\u003eA Solution\u003c/h3\u003e\n\u003cp\u003eJeremy wrote a function to help trim the cache and asked when it would be appropriate to apply it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etrimCache\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ecacheName\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003emaxItems\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecacheName\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        .\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                .\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003egt\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003emaxItems\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#66d9ef\"\u003edelete\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e])\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            .\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003etrimCache\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecacheName\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003emaxItems\u003c/span\u003e));\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e};\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd that got me thinking. In what situations is this problem more likely to occur? This particular problem happens when a lot of files are being called asynchronously. This problem doesn’t occur when only one file is being loaded. So when do we load a bunch of files? During page load. During page load, the browser might request css, javascript, images, etc. Which for most \u003ca href=\"https://web.archive.org/web/20151103105513/http://royal.pingdom.com/2011/11/21/web-pages-getting-bloated-here-is-why/\"\u003ewebsites\u003c/a\u003e, is a lot of files. Let’s now move our focus back to the humble script.js. Before, the only role it played with service workers was registering the script. However, if we can get the script to notify the service worker when the page is done loading, then the service worker will know when to trim the cache.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;serviceWorker\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ein\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003enavigator\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003enavigator\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eserviceWorker\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eregister\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;https://yourwebsite.com/serviceworker.js\u0026#39;\u003c/span\u003e, {\u003cspan style=\"color:#a6e22e\"\u003escope\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/\u0026#39;\u003c/span\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewindow.\u003cspan style=\"color:#a6e22e\"\u003eaddEventListener\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;load\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003enavigator\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eserviceWorker\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003econtroller\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003enavigator\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eserviceWorker\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003econtroller\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003epostMessage\u003c/span\u003e({\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;command\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;trimCache\u0026#34;\u003c/span\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWhy \u003ccode\u003eif (navigator.serviceWorker.controller != null)\u003c/code\u003e Service Workers don’t take control of the page immediately but on subsequent page loads, Jake Archibald \u003ca href=\"https://jakearchibald.com/2014/using-serviceworker-today/\"\u003eexplains\u003c/a\u003e. When the service worker does have control of the page however, we can use the \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage\"\u003epostMessage api\u003c/a\u003e to send a message to the service worker. Inside, I provided a json with a “command” to “trimCache”. Since we send the json to the service worker, we need to make sure that it can receive it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eself\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eaddEventListener\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;message\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edata\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003edata\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003edata\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ecommand\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;trimCache\u0026#34;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003etrimCache\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;pages\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e25\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003etrimCache\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;images\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003etrimCache\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;assets\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e30\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOnce it receives the command, it goes on to trim all of the caches.\u003c/p\u003e\n\u003ch3 id=\"conclusion\"\u003eConclusion\u003c/h3\u003e\n\u003cp\u003eSo whenever you download a bunch of files, make sure to run \u003ccode\u003enavigator.serviceWorker.controller.postMessage({\u0026quot;command\u0026quot;:\u0026quot;trimCache\u0026quot;});\u003c/code\u003e on the main javascript file to trim the cache. A downside to this method is that since Service Workers don’t take control during the first page load, the cache isn’t trimmed until the second page load. If you can find a way to make it so that this event happens in the first page load \u003ca href=\"mailto:brozek@brandonrozek.com\"\u003etell me\u003c/a\u003e about it/write a blog post. 🙂 \u003cstrong\u003eUpdate:\u003c/strong\u003e To get the service worker to take control of the page immediately call \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/skipWaiting\"\u003eself.skipWaiting()\u003c/a\u003e after the install event and \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/API/Clients/claim\"\u003eself.clients.claim()\u003c/a\u003e after the activate event. Current code for our humble service worker:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;v2.0.24:\u0026#39;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eofflineFundamentals\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/offline/\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//Add core website files to cache during serviceworker installation\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eupdateStaticCache\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;fundamentals\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e Promise.\u003cspan style=\"color:#a6e22e\"\u003eall\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eofflineFundamentals\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003emap\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003evalue\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eRequest\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003evalue\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eURL\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eorigin\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003elocation\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eorigin\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eRequest\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003evalue\u003c/span\u003e, {\u003cspan style=\"color:#a6e22e\"\u003emode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;no-cors\u0026#39;\u003c/span\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efetch\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e) { \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecachedCopy\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eclone\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eput\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003ecachedCopy\u003c/span\u003e); \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t})\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e};\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//Clear caches with a different version number\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclearOldCaches\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e().\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e Promise.\u003cspan style=\"color:#a6e22e\"\u003eall\u003c/span\u003e(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \t\t\t\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \t\t\t.\u003cspan style=\"color:#a6e22e\"\u003efilter\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ekey\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e              \t\t\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ekey\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \t\t\t})\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \t\t\t.\u003cspan style=\"color:#a6e22e\"\u003emap\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ekey\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e              \t\t\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#66d9ef\"\u003edelete\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ekey\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \t\t\t})\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \t\t);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t})\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/*\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\ttrims the cache\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\tIf cache has more than maxItems then it removes the excess items starting from the beginning\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etrimCache\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ecacheName\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003emaxItems\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecacheName\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        .\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                .\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emaxItems\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#66d9ef\"\u003edelete\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e])\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            .\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003etrimCache\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecacheName\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003emaxItems\u003c/span\u003e));\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e};\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//When the service worker is first added to a computer\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eself\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eaddEventListener\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;install\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ewaitUntil\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eupdateStaticCache\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t.\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() { \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eself\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eskipWaiting\u003c/span\u003e(); \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t})\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e})\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eself\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eaddEventListener\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;message\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edata\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003edata\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e//Send this command whenever many files are downloaded (ex: a page load)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003edata\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ecommand\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;trimCache\u0026#34;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003etrimCache\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;pages\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e25\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003etrimCache\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;images\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003etrimCache\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;assets\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e30\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//Service worker handles networking\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eself\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eaddEventListener\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;fetch\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e//Fetch from network and cache\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efetchFromNetwork\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecacheCopy\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eclone\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Accept\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;text/html\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;pages\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eput\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003ecacheCopy\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t} \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Accept\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;image\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;images\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eput\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003ecacheCopy\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t} \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;assets\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eput\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003ecacheCopy\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e//Fetch from network failed\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\t\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efallback\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Accept\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;text/html\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ematch\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e) { \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ematch\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/offline/\u0026#39;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t})\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t} \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Accept\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;image\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eResponse\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Offlineoffline\u0026#39;\u003c/span\u003e, { \u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e { \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Content-Type\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;image/svg+xml\u0026#39;\u003c/span\u003e }});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t} \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e//This service worker won\u0026#39;t touch non-get requests\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003emethod\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;GET\u0026#39;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e//For HTML requests, look for file in network, then cache if network fails.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Accept\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;text/html\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t    \t\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erespondWith\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003efetch\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003efetchFromNetwork\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003efallback\u003c/span\u003e));\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e//For non-HTML requests, look for file in cache, then network if no cache exists.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\t\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erespondWith\u003c/span\u003e(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ematch\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecached\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecached\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efetch\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003efetchFromNetwork\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003efallback\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t})\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t)\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//After the install event\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eself\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eaddEventListener\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;activate\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ewaitUntil\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eclearOldCaches\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t.\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() { \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eself\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eclients\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eclaim\u003c/span\u003e(); \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\t})\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;serviceWorker\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ein\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003enavigator\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003enavigator\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eserviceWorker\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eregister\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;https://brandonrozek.com/serviceworker.js\u0026#39;\u003c/span\u003e, {\u003cspan style=\"color:#a6e22e\"\u003escope\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/\u0026#39;\u003c/span\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewindow.\u003cspan style=\"color:#a6e22e\"\u003eaddEventListener\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;load\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003enavigator\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eserviceWorker\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003econtroller\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#a6e22e\"\u003enavigator\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eserviceWorker\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003econtroller\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003epostMessage\u003c/span\u003e({\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;command\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;trimCache\u0026#34;\u003c/span\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2015.11.30",
    "tags": ["Web","JS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2015-11-15-fractions-js/",
    "url": "https://brandonrozek.com/blog/2015-11-15-fractions-js/",
    "title": "Math with Fractions.js",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eLast week I published my first library over on Github called \u003ca href=\"https://github.com/brandonrozek/Fractions.js\"\u003eFractions.js\u003c/a\u003e. Fractions.js is a library to help avoid the \u003ca href=\"http://floating-point-gui.de/\"\u003emathmatetical errors\u003c/a\u003e in floating point arithmetic. What do you mean by floating point artihmetic errors? Here is an example: \u003ccode\u003e.1 * .2\u003c/code\u003e outputs \u003ccode\u003e0.020000000000000004\u003c/code\u003e even though the correct answer is \u003ccode\u003e.02\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"purpose\"\u003ePurpose\u003c/h2\u003e\n\u003cp\u003eMy team and I are currently working on a new game for Math I/O. One of the levels is testing whether the player can add, subtract, multiply, and divide fractions correctly. I didn’t want to implement a solution where we check if the input entered is within a range of the answer. So I asked the team, how do you add, subtract, multiply, and divide fractions in your head? As we were working through it together, I realized that this is a common problem over at Math I/O. So I decided to make a library dedicated to this.\u003c/p\u003e\n\u003ch2 id=\"how-it-works\"\u003eHow it works\u003c/h2\u003e\n\u003cp\u003eI broke up each fraction into two things, a numerator and a denominator. With these two numbers, I can figure out all of the most common operations.\u003c/p\u003e\n\u003ch3 id=\"addition\"\u003eAddition\u003c/h3\u003e\n\u003cp\u003eFor addition, if two fractions have the same denominator, then you just need to add the numerators.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e1/3 + 1/3 = 2/3\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eIf not, then you need to change it to have a common denominator. We can do this by multiply each fractions numerator and denominator by the other fractions denominator.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e1/2 + 1/3 = (1 * 3)/ (2 * 3) + (1 * 2)/ (2 * 3) = 3/6 + 2/6 = 5/6\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"subtraction\"\u003eSubtraction\u003c/h3\u003e\n\u003cp\u003eWorks the same as addition, except the second fraction is subtracted (taken away) from the first.\u003c/p\u003e\n\u003ch3 id=\"multiplication\"\u003eMultiplication\u003c/h3\u003e\n\u003cp\u003eTo multiply two fractions, just multiply the numerators by each other, and the denominators by each other.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e2/3 * 1/2 = 2/6\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"division\"\u003eDivision\u003c/h3\u003e\n\u003cp\u003eTreated similar to multiplication since dividing a number is the same thing as multiplying by it’s \u003ca href=\"https://www.mathsisfun.com/reciprocal.html\"\u003ereciprocal\u003c/a\u003e.\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e1 / (1 / 2) = 1 * 2 = 2\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"simplification\"\u003eSimplification\u003c/h3\u003e\n\u003cp\u003eSometimes with the operations above, it’ll produce fractions in an unsimplified form. To avoid any confusion, I created a simplify function. It was challanging trying to figure out a way to code this. While I was browsing around for an implementation, I knocked into Euclider’s algorithm for finding the greatest common factor. Straight from the \u003ca href=\"https://en.wikipedia.org/wiki/Euclidean_algorithm\"\u003eWikipedia article\u003c/a\u003e (where a is greater than b):\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efunction gcd(a, b)\n    while b ≠ 0\n       t := b;\n       b := a mod b;\n       a := t;\n    return a;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eI can then simplify the fraction by dividing the numerator and denominator by the greatest common factor.\u003c/p\u003e\n\u003ch2 id=\"the-api\"\u003eThe API\u003c/h2\u003e\n\u003cp\u003eI decided to provide as much flexibility as I can in the API. You have several ways to create a new Fraction.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eoneHalf\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eFraction\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eoneHalf\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eFraction\u003c/span\u003e(.\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eoneHalf\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eFraction\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;1/2\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eoneHalf\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eFraction\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;1\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAll of these results will return a Fraction with a numerator of \u003ccode\u003e1\u003c/code\u003e and a denominator of \u003ccode\u003e2\u003c/code\u003e. You also have two different ways to do the most common operations.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efiveThirds\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eFraction\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;1/3\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;4/3\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efiveThirds\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eFraction\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;1/3\u0026#34;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;4/3\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe second style came from how jQuery implements it’s library. That way you can chain operations.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eFraction\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;1/2\u0026#34;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2/3\u0026#34;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003edivide\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;5/6\u0026#34;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003esubtract\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;7/8\u0026#34;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003emultiply\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;6/5\u0026#34;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003etoString\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOutputs \u003ccode\u003e'63/100'\u003c/code\u003e This is accomplished in the code through \u003ca href=\"http://javascriptissexy.com/javascript-prototype-in-plain-detailed-language/\"\u003eprototypes\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eFraction\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003efrac1\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003efrac2\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eFraction\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ecorrectArgumentLength\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003earguments\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003efrac1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eFraction\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003etoFraction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003efrac1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003efrac2\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eFraction\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003etoFraction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003efrac2\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003enewFrac\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efrac1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003enewFrac\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003enumerator\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efrac1\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003enumerator\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efrac2\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003edenominator\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efrac1\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003edenominator\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efrac2\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003enumerator\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003enewFrac\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003edenominator\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efrac1\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003edenominator\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efrac2\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003edenominator\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eFraction\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003esimplify\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003enewFrac\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eFraction\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eprototype\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003efrac\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eFraction\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ecorrectArgumentLength\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003earguments\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eFraction\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003echange\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003eFraction\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003efrac\u003c/span\u003e));\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIn the code, the add prototype calls the Fraction.add function within it to avoid code duplication.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eAfter I coded this up, I looked online for different implementations and found \u003ca href=\"https://github.com/ekg/fraction.js\"\u003efraction.js\u003c/a\u003e by \u003ca href=\"http://hypervolu.me/~erik/\"\u003eErik Garrison\u003c/a\u003e. It’s important to look at different implementations and see which matches your needs better. This post isn’t meant to go fully into detail of the library. To know what else the library can do, visit the \u003ca href=\"https://github.com/brandonrozek/Fractions.js/blob/master/README.md\"\u003ereadme page\u003c/a\u003e. If you’re curious in how it’s implemented, check out the \u003ca href=\"https://github.com/brandonrozek/Fractions.js/blob/master/Fraction.js\"\u003ecode\u003c/a\u003e. \u003ca href=\"mailto:brozek@brandonrozek.com\"\u003eEmail me\u003c/a\u003e if you have any questions/criticisms 🙂\u003c/p\u003e",
    "date_published": "2015.11.15",
    "tags": ["JS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2015-11-14-service-workers/",
    "url": "https://brandonrozek.com/blog/2015-11-14-service-workers/",
    "title": "An Offline Experience with Service Workers",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI’m excited to say that I’ve written my first service worker for brandonrozek.com. What is a service worker? A service worker provides an extra layer between the client and the server. The exciting part about this is that you can use service workers to deliver an offline experience. (Cached versions of your site, offline pages, etc.)\u003c/p\u003e\n\u003cp\u003eService workers are currently supported in Chrome, Opera, and Firefox nightly. You don’t have to worry too much about browser support because the Service Worker spec was written in a \u003ca href=\"http://alistapart.com/article/understandingprogressiveenhancement\"\u003eprogressively enchanced\u003c/a\u003e way meaning it won’t break your existing site 🙂\u003c/p\u003e\n\u003ch1 id=\"caveats\"\u003eCaveats\u003c/h1\u003e\n\u003cp\u003eYou need HTTPS to be able to use service workers on your site. This is mainly for security reasons. Imagine if a third party can control all of the networking requests on your site? If you don’t want to go out and buy a SSL Certificate, there are a couple free ways to go about this.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca href=\"https://www.cloudflare.com/\"\u003eCloudflare\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://letsencrypt.org/\"\u003eLet’s Encrypt\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eService workers are promise heavy. Promises contain a then clause which runs code asynchronously. If you’re not accustomed to this idea please check out this \u003ca href=\"https://ponyfoo.com/articles/es6-promises-in-depth\"\u003epost\u003c/a\u003e by Nicolas Bevacqua. Now onto making the service worker! If you want to skip to the final code scroll down to the bottom. If you don’t like my syntax highlighting, then you can check out this \u003ca href=\"https://gist.github.com/brandonrozek/0cf038df40a913fda655\"\u003egist\u003c/a\u003e.\u003c/p\u003e\n\u003ch1 id=\"register-the-service-worker\"\u003eRegister the service worker\u003c/h1\u003e\n\u003cp\u003ePlace \u003ccode\u003eservice-worker.js\u003c/code\u003e on the root of your site. This is so the service worker can access all the files in the site. Then in your main javascript file, register the service worker.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003enavigator\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eserviceWorker\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003enavigator\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eserviceWorker\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eregister\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/serviceworker.js\u0026#39;\u003c/span\u003e, {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003escope\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch1 id=\"install-the-service-worker\"\u003eInstall the service worker\u003c/h1\u003e\n\u003cp\u003eThe first time the service worker runs, it emits the \u003ccode\u003einstall\u003c/code\u003e event. At this time, we can load the visitor’s cache with some resources for when they’re offline. Every so often, I like to change up the theme of the site. So I have version numbers attached to my files. I would also like to invalidate my cache with this version number. So at the top of the file I added\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;v2.0.24:\u0026#39;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow, to specify which files I want the service worker to cache for offline use. I thought my home page and my offline page would be good enough.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eofflineFundamentals\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/offline/\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e];\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSince \u003ccode\u003ecache.addAll()\u003c/code\u003e hasn’t been implemented yet in any of the browsers, and the polyfill implementation didn’t work for my needs. I pieced together my own.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eupdateStaticCache\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;fundamentals\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e Promise.\u003cspan style=\"color:#a6e22e\"\u003eall\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eofflineFundamentals\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003emap\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003evalue\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eRequest\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003evalue\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eURL\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eorigin\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003elocation\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eorigin\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eRequest\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003evalue\u003c/span\u003e, {\u003cspan style=\"color:#a6e22e\"\u003emode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;no-cors\u0026#39;\u003c/span\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efetch\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecachedCopy\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eclone\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eput\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003ecachedCopy\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e};\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLet’s go through this chunk of code.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eOpen the cache called \u003ccode\u003e'v2.0.24:fundamentals'\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eGo through all of the \u003ccode\u003eofflineFundamental\u003c/code\u003e‘s URLs\n\u003cul\u003e\n\u003cli\u003eDoes the file I ask for come from the same domain as my site?\u003c/li\u003e\n\u003cli\u003eNo. Then, make the request ‘no-cors’ (I had difficulty getting my asset files in cors mode. If the cors headers are included in the response, then you can take out this line)\u003c/li\u003e\n\u003cli\u003eFetch the file from the network and then cache it.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eNow we call it when the \u003ccode\u003einstall\u003c/code\u003e event is fired.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eself\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eaddEventListener\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;install\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ewaitUntil\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eupdateStaticCache\u003c/span\u003e())\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e})\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWith this we now cached all the files in the offlineFundamentals array during the install step.\u003c/p\u003e\n\u003ch1 id=\"clear-out-the-old-cache\"\u003eClear out the old cache\u003c/h1\u003e\n\u003cp\u003eSince we’re caching everything. If you change one of the files, your visitor wouldn’t get the changed file. Wouldn’t it be nice to remove old files from the visitor’s cache? Every time the service worker finishes the install step, it releases an \u003ccode\u003eactivate\u003c/code\u003e event. We can use this to look and see if there are any old cache containers on the visitor’s computer. From \u003ca href=\"https://ponyfoo.com/articles/serviceworker-revolution\"\u003eNicolas’ code\u003c/a\u003e. Thanks for sharing 🙂\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclearOldCaches\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e().\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e Promise.\u003cspan style=\"color:#a6e22e\"\u003eall\u003c/span\u003e(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                      \u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        .\u003cspan style=\"color:#a6e22e\"\u003efilter\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ekey\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                              \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ekey\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        .\u003cspan style=\"color:#a6e22e\"\u003emap\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ekey\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                              \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#66d9ef\"\u003edelete\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ekey\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                );\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col\u003e\n\u003cli\u003eCheck the names of each of the cache containers\u003c/li\u003e\n\u003cli\u003eIf they don’t start with the correct version number\n\u003cul\u003e\n\u003cli\u003eDelete that cache container\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eCall the function when the \u003ccode\u003eactivate\u003c/code\u003e event fires.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eself\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eaddEventListener\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;activate\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ewaitUntil\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eclearOldCaches\u003c/span\u003e())\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch1 id=\"intercepting-fetch-requests\"\u003eIntercepting fetch requests\u003c/h1\u003e\n\u003cp\u003eThe cool thing about service worker’s is that it can handle file requests. We could cache all files requested for offline use, and if a fetch for a resource failed, then the service worker can look for it in the cache or provide an alternative. This is a large section, so I’m going to attempt to break it down as much as I can.\u003c/p\u003e\n\u003ch2 id=\"limit-the-cache\"\u003eLimit the cache\u003c/h2\u003e\n\u003cp\u003eIf the visitor started browsing all of the pages on my site, his or her cache would start to get bloated with files. To not burden my visitors, I decided to only keep the latest 25 pages and latest 10 images in the cache.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003elimitCache\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003emaxItems\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e().\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emaxItems\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#66d9ef\"\u003edelete\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eWe’ll call it later in the code.\u003c/p\u003e\n\u003ch2 id=\"fetch-from-network-and-cache\"\u003eFetch from network and cache\u003c/h2\u003e\n\u003cp\u003eEvery time I fetch a file from the network I throw it into a specific cache container: \u003ccode\u003e'pages'\u003c/code\u003e for HTML files, \u003ccode\u003e'images'\u003c/code\u003e for CSS files, and \u003ccode\u003e'assets'\u003c/code\u003e for any other file. This is so I can handle the cache limiting above easier. Defined within the \u003ccode\u003efetch\u003c/code\u003e event.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efetchFromNetwork\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecacheCopy\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eclone\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Accept\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;text/html\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;pages\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eput\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003ecacheCopy\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#a6e22e\"\u003elimitCache\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e25\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        } \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Accept\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;image\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;images\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eput\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003ecacheCopy\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#a6e22e\"\u003elimitCache\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e); \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        } \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;assets\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eput\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003ecacheCopy\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"when-the-network-fails\"\u003eWhen the network fails\u003c/h2\u003e\n\u003cp\u003eThere are going to be times where the visitor cannot access the website. Maybe they went in a tunnel while they were riding a train? Or maybe your site went down. I thought it would be nice for my reader’s to be able to look over my blog posts again regardless of an internet connection. So I provide a fall-back. Defined within the \u003ccode\u003efetch\u003c/code\u003e event.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efallback\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Accept\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;text/html\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ematch\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e) { \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ematch\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/offline/\u0026#39;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        } \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Accept\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;image\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eResponse\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u0026amp;lt;svg width=\u0026#34;400\u0026#34; height=\u0026#34;300\u0026#34; role=\u0026#34;img\u0026#34; aria-labelledby=\u0026#34;offline-title\u0026#34; viewBox=\u0026#34;0 0 400 300\u0026#34; xmlns=\u0026#34;http://www.w3.org/2000/svg\u0026#34;\u0026amp;gt;\u0026amp;lt;title id=\u0026#34;offline-title\u0026#34;\u0026amp;gt;Offline\u0026amp;lt;/title\u0026amp;gt;\u0026amp;lt;g fill=\u0026#34;none\u0026#34; fill-rule=\u0026#34;evenodd\u0026#34;\u0026amp;gt;\u0026amp;lt;path fill=\u0026#34;#D8D8D8\u0026#34; d=\u0026#34;M0 0h400v300H0z\u0026#34;/\u0026amp;gt;\u0026amp;lt;text fill=\u0026#34;#9B9B9B\u0026#34; font-family=\u0026#34;Helvetica Neue,Arial,Helvetica,sans-serif\u0026#34; font-size=\u0026#34;72\u0026#34; font-weight=\u0026#34;bold\u0026#34;\u0026amp;gt;\u0026amp;lt;tspan x=\u0026#34;93\u0026#34; y=\u0026#34;172\u0026#34;\u0026amp;gt;offline\u0026amp;lt;/tspan\u0026amp;gt;\u0026amp;lt;/text\u0026amp;gt;\u0026amp;lt;/g\u0026amp;gt;\u0026amp;lt;/svg\u0026amp;gt;\u0026#39;\u003c/span\u003e, { \u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e { \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Content-Type\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;image/svg+xml\u0026#39;\u003c/span\u003e }});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        } \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col\u003e\n\u003cli\u003eIs the request for a HTML file?\n\u003cul\u003e\n\u003cli\u003eShow the \u003ca href=\"https://brandonrozek.com/offline/\"\u003eoffline\u003c/a\u003e page.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003eIs the request for an image?\n\u003cul\u003e\n\u003cli\u003eShow a place-holder image (Courtesy of \u003ca href=\"https://adactio.com/journal/9775\"\u003eJeremy Keith\u003c/a\u003e)\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"handle-the-request\"\u003eHandle the request\u003c/h2\u003e\n\u003cp\u003eFirst off, I’m only handling GET requests.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003emethod\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;GET\u0026#39;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor HTML files, grab the file from the network. If that fails, then look for it in the cache. \u003cem\u003eNetwork then cache strategy\u003c/em\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Accept\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;text/html\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erespondWith\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003efetch\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003efetchFromNetwork\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003efallback\u003c/span\u003e));\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor non-HTML files, follow this series of steps\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eCheck the cache\u003c/li\u003e\n\u003cli\u003eDoes a cache exist for this file?\n\u003cul\u003e\n\u003cli\u003eYes. Then show it\u003c/li\u003e\n\u003cli\u003eNo. Then grab it from the network and cache it.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cem\u003eCache then network strategy\u003c/em\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erespondWith\u003c/span\u003e(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ematch\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecached\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecached\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efetch\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003efetchFromNetwork\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003efallback\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor different stategy’s, take a look at Jake Archibald’s \u003ca href=\"https://jakearchibald.com/2014/offline-cookbook/\"\u003eoffline cookbook\u003c/a\u003e.\u003c/p\u003e\n\u003ch1 id=\"conclusion\"\u003eConclusion\u003c/h1\u003e\n\u003cp\u003eWith all of that, we now have a fully functioning offline-capable website! I wouldn’t be able to implement this myself if it wasn’t for some of the awesome people I mentioned earlier sharing their experience. So share, share, share! With that sentiment, I’ll now share the full code for \u003ccode\u003eservice-worker.js\u003c/code\u003e \u003cstrong\u003eUpdate:\u003c/strong\u003e There is a new version of this code over at this \u003ca href=\"https://brandonrozek.com/2015/11/limiting-cache-service-workers-revisited/\"\u003eblog post\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;v2.0.24:\u0026#39;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eofflineFundamentals\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/offline/\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//Add core website files to cache during serviceworker installation\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eupdateStaticCache\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;fundamentals\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e Promise.\u003cspan style=\"color:#a6e22e\"\u003eall\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eofflineFundamentals\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003emap\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003evalue\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eRequest\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003evalue\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eURL\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eurl\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eorigin\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003elocation\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eorigin\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eRequest\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003evalue\u003c/span\u003e, {\u003cspan style=\"color:#a6e22e\"\u003emode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;no-cors\u0026#39;\u003c/span\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efetch\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecachedCopy\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eclone\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eput\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003ecachedCopy\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e};\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//Clear caches with a different version number\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclearOldCaches\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e().\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e Promise.\u003cspan style=\"color:#a6e22e\"\u003eall\u003c/span\u003e(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                      \u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        .\u003cspan style=\"color:#a6e22e\"\u003efilter\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ekey\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                              \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ekey\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        .\u003cspan style=\"color:#a6e22e\"\u003emap\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003ekey\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                              \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#66d9ef\"\u003edelete\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ekey\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                );\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e/*\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e    limits the cache\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e    If cache has more than maxItems then it removes the first item in the cache\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e*/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003elimitCache\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003emaxItems\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ekeys\u003c/span\u003e().\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emaxItems\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#66d9ef\"\u003edelete\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eitems\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//When the service worker is first added to a computer\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eself\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eaddEventListener\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;install\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ewaitUntil\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eupdateStaticCache\u003c/span\u003e())\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e})\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//Service worker handles networking\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eself\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eaddEventListener\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;fetch\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e//Fetch from network and cache\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efetchFromNetwork\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecacheCopy\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eclone\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Accept\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;text/html\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;pages\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eput\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003ecacheCopy\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#a6e22e\"\u003elimitCache\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e25\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        } \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Accept\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;image\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;images\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eput\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003ecacheCopy\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#a6e22e\"\u003elimitCache\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        } \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eversion\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;assets\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#a6e22e\"\u003ecache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eput\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003ecacheCopy\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e//Fetch from network failed\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efallback\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Accept\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;text/html\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ematch\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e) { \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eresponse\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ematch\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;/offline/\u0026#39;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        } \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Accept\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;image\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eResponse\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u0026amp;lt;svg width=\u0026#34;400\u0026#34; height=\u0026#34;300\u0026#34; role=\u0026#34;img\u0026#34; aria-labelledby=\u0026#34;offline-title\u0026#34; viewBox=\u0026#34;0 0 400 300\u0026#34; xmlns=\u0026#34;http://www.w3.org/2000/svg\u0026#34;\u0026amp;gt;\u0026amp;lt;title id=\u0026#34;offline-title\u0026#34;\u0026amp;gt;Offline\u0026amp;lt;/title\u0026amp;gt;\u0026amp;lt;g fill=\u0026#34;none\u0026#34; fill-rule=\u0026#34;evenodd\u0026#34;\u0026amp;gt;\u0026amp;lt;path fill=\u0026#34;#D8D8D8\u0026#34; d=\u0026#34;M0 0h400v300H0z\u0026#34;/\u0026amp;gt;\u0026amp;lt;text fill=\u0026#34;#9B9B9B\u0026#34; font-family=\u0026#34;Helvetica Neue,Arial,Helvetica,sans-serif\u0026#34; font-size=\u0026#34;72\u0026#34; font-weight=\u0026#34;bold\u0026#34;\u0026amp;gt;\u0026amp;lt;tspan x=\u0026#34;93\u0026#34; y=\u0026#34;172\u0026#34;\u0026amp;gt;offline\u0026amp;lt;/tspan\u0026amp;gt;\u0026amp;lt;/text\u0026amp;gt;\u0026amp;lt;/g\u0026amp;gt;\u0026amp;lt;/svg\u0026amp;gt;\u0026#39;\u003c/span\u003e, { \u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e { \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Content-Type\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;image/svg+xml\u0026#39;\u003c/span\u003e }});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e//This service worker won\u0026#39;t touch non-get requests\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003emethod\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;GET\u0026#39;\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e//For HTML requests, look for file in network, then cache if network fails.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheaders\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eget\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;Accept\u0026#39;\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;text/html\u0026#39;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erespondWith\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003efetch\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003efetchFromNetwork\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003efallback\u003c/span\u003e));\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e//For non-HTML requests, look for file in cache, then network if no cache exists.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    \u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erespondWith\u003c/span\u003e(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003ecaches\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ematch\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecached\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecached\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e||\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efetch\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003erequest\u003c/span\u003e).\u003cspan style=\"color:#a6e22e\"\u003ethen\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003efetchFromNetwork\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003efallback\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        })\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    )\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//After the install event\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eself\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eaddEventListener\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;activate\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ewaitUntil\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eclearOldCaches\u003c/span\u003e())\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e});\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e",
    "date_published": "2015.11.14",
    "tags": ["Web","JS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2015-10-25-functions/",
    "url": "https://brandonrozek.com/blog/2015-10-25-functions/",
    "title": "Functions in Javascript",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eEver had a snippet of code that appears multiple times in different places in your program? Whenever you had to change that snippet, you end up playing this game of search and replace. Functions can help. They exist to abstract your code, making it not only easier to change that little snippet, but to read and debug your code as well.\u003c/p\u003e\n\u003ch3 id=\"how-to-createexecute-a-function\"\u003eHow to create/execute a function\u003c/h3\u003e\n\u003cp\u003eTo make a function\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edoSomething\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003edoStuff\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo call the above function to execute\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003edoSomething\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"arguments\"\u003eArguments\u003c/h3\u003e\n\u003cp\u003eYou can also add in arguments (parameters that go inside the paraenthesis next to the word function) for the functions to use.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003enumber1\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003enumber2\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003enumber1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003enumber2\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAnd when you use the \u003ccode\u003ereturn\u003c/code\u003e keyword, like the function above. You can store the value in a variable for future use.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etotal\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003etotal\u003c/code\u003e here will equal \u003ccode\u003e4\u003c/code\u003e\u003c/p\u003e\n\u003ch3 id=\"scope\"\u003eScope\u003c/h3\u003e\n\u003cp\u003eFunctions create their own scope, which means that variables created inside the function will only be able available within that function.\u003c/p\u003e\n\u003cp\u003eThe snippet below will output an error like \u003ccode\u003etotal is not defined\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eadd\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003enumber1\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003enumber2\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etotal\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003enumber1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003enumber2\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003etotal\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBelow is a correct example of the concept\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e//Function below converts km/hr to m/s\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003econvert\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003espeed\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emetersPerHour\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003espeed\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1000\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emetersPerSecound\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emetersPerHour\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e3600\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emetersPerSecond\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecurrentSpeed\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003econvert\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIt’s also important to note that functions can use variables outside of it; granted it resides in the same scope.\u003c/p\u003e\n\u003cp\u003eHere is an example of a variable that doesn\u0026rsquo;t reside in the same scope as the function. (The below code will fail)\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esomething\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ey\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003esomething\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eaddXandY\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ey\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003eaddXandY\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eBelow, is an example of where the variable does reside in the same scope as the function. Which allows this snippet to execute properly.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eaddX\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ea\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ea\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esum\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eaddX\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e6\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003esum\u003c/code\u003e here will equal \u003ccode\u003e11\u003c/code\u003e\u003c/p\u003e\n\u003ch3 id=\"conclusion\"\u003eConclusion\u003c/h3\u003e\n\u003cp\u003eAs long as you name them appropriately, functions are useful for abstracting your code, making them easier to understand. This concludes another lecture made for the members over at Math I/O. Until next week 🙂\u003c/p\u003e\n",
    "date_published": "2015.10.25",
    "tags": ["Web","JS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2015-10-18-javascript-conditional-statements/",
    "url": "https://brandonrozek.com/blog/2015-10-18-javascript-conditional-statements/",
    "title": "Javascript Conditional Statements",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eJavascript, like most other programming languages, include ways to run blocks of code when something meets a condition. Here, I will describe the most common ways to do so.\u003c/p\u003e\n\u003cp\u003eThis post is part of my lecture series for Math I/O. There is no pre-reading for this lecture.\u003c/p\u003e\n\u003ch3 id=\"if-statement\"\u003eIf Statement\u003c/h3\u003e\n\u003cp\u003eTo run a block of code when a condition is true, use an \u003ccode\u003eif\u003c/code\u003e statement.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003econdition\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003edoSomething\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can also run a block of code when a condition is false using the \u003ccode\u003eelse\u003c/code\u003e statement.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003econdition\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003edoSomething\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e} \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003edoSomethingElse\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"switch-statement\"\u003eSwitch statement\u003c/h3\u003e\n\u003cp\u003eIf you want to check a variable for \u003cstrong\u003eequality\u003c/strong\u003e against multiple different cases, use a \u003ccode\u003eswitch\u003c/code\u003e statement.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eswitch\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003evariable\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003econdition1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003edoSomething\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ebreak\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ecase\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003econdition2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003edoSomethingElse\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ebreak\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003edefault\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#a6e22e\"\u003edoSomethingCompletelyDifferent\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003ebreak\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThe default statement runs when the variable doesn’t equal any of the cases.\u003c/p\u003e\n\u003ch3 id=\"while-loop\"\u003eWhile loop\u003c/h3\u003e\n\u003cp\u003eTo run a block of code over and over again until a condition is false, use a \u003ccode\u003ewhile\u003c/code\u003e loop.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewhile\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003econdition\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003edoSomething\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eDon’t forget to include something in the loop that will eventually make the condition \u003ccode\u003efalse\u003c/code\u003e, otherwise you run into an infinite loop. (Which is a loop that never stops repeating itself; most likely crashing your browser)\u003c/p\u003e\n\u003ch3 id=\"for-loop\"\u003eFor loop\u003c/h3\u003e\n\u003cp\u003eIf you want to run something a certain amount of times, use a \u003ccode\u003efor\u003c/code\u003e loop. For loops can be broken down into three components: an initiating statement, a condition, and a statement that runs after every loop.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003elt\u003c/span\u003e; \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003edoSomething\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eHere you have the initiating statement of \u003ccode\u003evar i = 0\u003c/code\u003e. From there you check, is \u003ccode\u003ei\u003c/code\u003e less than 5? Yes, so then we \u003ccode\u003edoSomething();\u003c/code\u003e. After we \u003ccode\u003edoSomething();\u003c/code\u003e, we add 1 to \u003ccode\u003ei\u003c/code\u003e. Now \u003ccode\u003ei\u003c/code\u003e equals 2. Is\u003ccode\u003ei\u003c/code\u003e still less than 5? Yes, so we \u003ccode\u003edoSomething();\u003c/code\u003e. Then we add 1 to \u003ccode\u003ei\u003c/code\u003e again. This loop will keep happening until \u003ccode\u003ei\u003c/code\u003e is not less than 5.\u003c/p\u003e\n\u003ch3 id=\"conclusion\"\u003eConclusion\u003c/h3\u003e\n\u003cp\u003eHaving different control/conditional statements helps keep the state of any application you’re making. Did the user say not to notify them? Then don’t, otherwise (else) notify them. That’s all I have to say for this week. Hope this post helps you get a little more used to this big world called programming.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eyouLikeThisPost\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Come back next week! :)\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    } \u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Aww that\u0026#39;s okay, you should give me another chance next week :)\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eI recommend that you look at different perspectives of the same concepts. WebCheatSheet.com has a similar post to mine, check out what they had to say \u003ca href=\"http://webcheatsheet.com/javascript/if_else_switch.php\"\u003ehere\u003c/a\u003e.\u003c/p\u003e",
    "date_published": "2015.10.18",
    "tags": ["Web","JS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2015-10-18-animatable-transform/",
    "url": "https://brandonrozek.com/blog/2015-10-18-animatable-transform/",
    "title": "Animatable: Transform",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis is the last post of the animatable series. The grand finale. Here, we will talk about the transform property. It’s only one property but it comes with a lot of goodies in the form of transform-functions.\u003c/p\u003e\n\u003cp\u003eThis is not a comprehensize list. If you want one of those, please check out MDN. They have a great \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function\"\u003eresource\u003c/a\u003e. This is the last post of the animatable series, I recommend you check out the other parts too.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003ePart 1 — \u003ca href=\"https://brandonrozek.com/2015/05/animatable-border/\"\u003eAnimatable: Border\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003ePart 2 — \u003ca href=\"https://brandonrozek.com/2015/09/animatable-box-model/\"\u003eAnimatable: Box Model\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003ePart 3 — \u003ca href=\"https://brandonrozek.com/2015/10/animatable-text/\"\u003eAnimatable: Text\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003ePart 4 — \u003ca href=\"https://brandonrozek.com/2015/10/animatable-location/\"\u003eAnimatable: Location\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003ePart 5 — \u003ca href=\"https://brandonrozek.com/2015/10/animatable-visual/\"\u003eAnimatable: Visual\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eAnimatable: Transform goes with a \u003ca href=\"http://codepen.io/brandonrozek/full/ojoMyE\"\u003eCodePen demo\u003c/a\u003e{.broken_link} I made. Please check it out, as I will be referencing it later on in the post.\u003c/p\u003e\n\u003ch3 id=\"rotate\"\u003erotate()\u003c/h3\u003e\n\u003cp\u003eThe rotate() function moves the element around a fixed point as defined by the \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin\"\u003etransform-origin\u003c/a\u003e property. In the Codepen demo (#1), the star continously rotates 360 degrees.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003erotate\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003etransform\u003c/span\u003e: rotate(\u003cspan style=\"color:#ae81ff\"\u003e360\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003edeg\u003c/span\u003e);}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003erotate\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e6\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e6\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-image\u003c/span\u003e: url(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://upload.wikimedia.org/wikipedia/commons/thumb/5/51/Star_full.svg/2000px-Star_full.svg.png\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-size\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003ecover\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003etransform\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enone\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: rotate \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"scale\"\u003escale()\u003c/h3\u003e\n\u003cp\u003eScale(sx, sy) modifies the size of the element by taking 2 arguments: sx and sy. Where sx is the multiplier that scales the element in the x-direction while sy is the multiplier that scales the element in the y-direction. In the CodePen demo (#2), the little ninja man scales to half his size and back to normal again.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003escale\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003etransform\u003c/span\u003e: scale(\u003cspan style=\"color:#ae81ff\"\u003e.5\u003c/span\u003e); }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003escale\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e6\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e6\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-image\u003c/span\u003e: url(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://upload.wikimedia.org/wikipedia/commons/7/71/Monocromaticman.JPG\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-size\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003econtain\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-repeat\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eno-repeat\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003etransform\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enone\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: scale \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"skew\"\u003eskew()\u003c/h3\u003e\n\u003cp\u003eSkew distorts the element by moving each point to a certain angle determined by it’s distance from the origin. In the CodePen demo (#3), the square skews -20 degrees, making it appear as a parallelogram.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003eskew\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003etransform\u003c/span\u003e: \u003cspan style=\"color:#a6e22e\"\u003eskew\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e-20\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003edeg\u003c/span\u003e); }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eskew\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e6\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e6\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-color\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003elightblue\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003etransform\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enone\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: skew \u003cspan style=\"color:#ae81ff\"\u003e.75\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"translate\"\u003etranslate()\u003c/h3\u003e\n\u003cp\u003eTransform(tx, ty) moves the element as specified by it’s two parameters tx and ty. Tx tells the browser how many units to move it in the x direction while ty tells the browser how many units to move the element in the y direction. In the CodePen demo (#4), the car moves forward 10rem and back again.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003etranslate\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003etransform\u003c/span\u003e: translate(\u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e);}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003etranslate\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e6\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e6\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-image\u003c/span\u003e: url(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;http://res.freestockphotos.biz/pictures/15/15685-illustration-of-a-red-cartoon-car-pv.png\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-size\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003econtain\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-repeat\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eno-repeat\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003etransform\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enone\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: translate \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"conclusion\"\u003eConclusion\u003c/h3\u003e\n\u003cp\u003eIt is now the end of the Animatable series. I am sad to see this first completed series of mine go. It’s okay though, I’m bound to think of another series to write for you guys in no time. My goal in Animatable, is to show you (and me) what is possible using CSS animations. With this small reference completed, we have a good overview of the different tools available. Now with our new toolbelt, go out and craft meaningful experinces!\u003c/p\u003e",
    "date_published": "2015.10.18",
    "tags": ["Web","CSS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2015-10-11-animatable-visual/",
    "url": "https://brandonrozek.com/blog/2015-10-11-animatable-visual/",
    "title": "Animatable: Visual",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eHello everyone! This is part 5 of my series on animation. Today’s post will be short, since we’re only going to talk about color and opacity.\u003c/p\u003e\n\u003cp\u003eI’ll give a shout-out to \u003ca href=\"http://csstriggers.com\"\u003ehttp://csstriggers.com\u003c/a\u003e, if you are interested in \u003ca href=\"https://blogs.adobe.com/webplatform/2014/03/18/css-animations-and-transitions-performance/\"\u003eCSS Animation performance\u003c/a\u003e, then check them out to see what triggers a repaint and/or reflow. Take a look at the other posts in this series!\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003ePart 1 — \u003ca href=\"https://brandonrozek.com/2015/05/animatable-border/\"\u003eAnimatable: Border\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003ePart 2 — \u003ca href=\"https://brandonrozek.com/2015/09/animatable-box-model/\"\u003eAnimatable: Box Model\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003ePart 3 — \u003ca href=\"https://brandonrozek.com/2015/10/animatable-text/\"\u003eAnimatable: Text\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003ePart 4 — \u003ca href=\"https://brandonrozek.com/2015/10/animatable-location/\"\u003eAnimatable: Location\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThis post goes with a \u003ca href=\"http://codepen.io/brandonrozek/full/rOzeyO/\"\u003eCodepen demo\u003c/a\u003e{.broken_link} I made, I’ll reference it later in this post.\u003c/p\u003e\n\u003ch3 id=\"color\"\u003eColor\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts any \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/color_value\"\u003ecolor\u003c/a\u003e value\u003c/li\u003e\n\u003cli\u003eTypically inherits its color from the parent element\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThe color property sets the color of an element’s text content and its decoration. During the animation, the browser sees the colors in their red, green, and blue (rgb) components, then increments/decrements those values until it reaches the color it’s animating to. For example, in the Codepen demo (#1), the color of the text is changing from \u003ccode\u003ered\u003c/code\u003e or \u003ccode\u003ergb(255, 0, 0)\u003c/code\u003e to \u003ccode\u003egreen\u003c/code\u003e or \u003ccode\u003ergb(0, 255, 0)\u003c/code\u003e. Meaning the red component is going from 255 to 0 and the green component is going from 0 to 255 during the animation.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003ecolor\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003ecolor\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003egreen\u003c/span\u003e; }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003ecolor\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003efont-size\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ecolor\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003ered\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003etext-decoration\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eunderline\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003ecolor\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1.5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease-in\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"opacity\"\u003eopacity\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts any number from [0, 1]\u003c/li\u003e\n\u003cli\u003eInitial Value: 1\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eOpacity sets the transparency of an element and it’s decendants. Unlike \u003ccode\u003edisplay: none;\u003c/code\u003e, when the element is \u003ccode\u003eopacity: 0;\u003c/code\u003e the element still holds it’s space on the page. In the Codepen demo (#2), you can see the element and it’s children fading out.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003eopacity\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003eopacity\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopacity\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-color\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e#cd86e4\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eopacity\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eopacity\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003elinear\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopacity\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003emargin-left\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eauto\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003emargin-right\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eauto\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-color\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003elightblue\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eopacity\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003ediv\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003emargin-left\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eauto\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003emargin-right\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eauto\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-color\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e#00b300\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"conclusion\"\u003eConclusion\u003c/h3\u003e\n\u003cp\u003eI used to work on a chat application called Babbler. In it, I used opacity to fade in the messages as the user recieved them. With all these different types of animations, you can visually show the user what’s going on in your website/application. Doing this series helps me, (as well as you, I hope) recognize all the different properties and possibilities for animation. This is the second to last part of this series, meaning that the next part is the finale. I’m somewhat sad to see this series ending but excited at the same time. Until next time, have fun animating. 🙂\u003c/p\u003e",
    "date_published": "2015.10.11",
    "tags": ["Web","CSS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2015-10-10-javascript-data-types/",
    "url": "https://brandonrozek.com/blog/2015-10-10-javascript-data-types/",
    "title": "Javascript Data Types",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eJavascript has multiple ways you can store your data. Each of these different ways is called a data type, and they each carry different “methods” which are helpful commands. Today, I’ll show you the different data types and methods that I use and how they’re useful.\u003c/p\u003e\n\u003cp\u003eThis post is by far not a comprehenive list of all the Data types and their methods. If you want one of those, please check out \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects\"\u003eMDN\u003c/a\u003e and/or \u003ca href=\"http://docs.webplatform.org/wiki/javascript/objects\"\u003eWebPlatform\u003c/a\u003e{.broken_link}. This is the second lecture of the web development class I’m teaching for the newer folks over at \u003ca href=\"http://math-io.com\"\u003eMath I/O\u003c/a\u003e. Due to the nature of Math I/O (making math games and all), the next few posts will be Javascript centric. We’re getting ready to build a new game, so I want to prepare them as much as possible. *Excited* ^_^ Ilya Kantor does a good job of descibing Javascript data types and their many methods in \u003ca href=\"http://javascript.info/tutorial/mastering-data-types\"\u003eMastering Data Types\u003c/a\u003e which I made the recommended reading for this lecture.\u003c/p\u003e\n\u003ch3 id=\"string\"\u003eString\u003c/h3\u003e\n\u003cp\u003eA string is one or more characters.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou can access a character inside of a string by using [] notation. Inside the [] you put the index of the character you want. An index is the numeral location of the character starting from the left. It is important to note that Javascript starts counting from 0.\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003eB\u003c/th\u003e\n          \u003cth\u003er\u003c/th\u003e\n          \u003cth\u003ea\u003c/th\u003e\n          \u003cth\u003en\u003c/th\u003e\n          \u003cth\u003ed\u003c/th\u003e\n          \u003cth\u003eo\u003c/th\u003e\n          \u003cth\u003en\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e0\u003c/td\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n          \u003ctd\u003e2\u003c/td\u003e\n          \u003ctd\u003e3\u003c/td\u003e\n          \u003ctd\u003e4\u003c/td\u003e\n          \u003ctd\u003e5\u003c/td\u003e\n          \u003ctd\u003e6\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efirstInitial\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e];\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNow the value of firstInitial is the letter \u003ccode\u003e\u0026quot;B\u0026quot;\u003c/code\u003e.\u003c/p\u003e\n\u003ch4 id=\"some-useful-methods-for-strings\"\u003eSome useful methods for strings\u003c/h4\u003e\n\u003ch5 id=\"stringprototypeindexof\"\u003e\u003ccode\u003eString.prototype.indexOf\u003c/code\u003e\u003c/h5\u003e\n\u003cp\u003eThis can be used to find the index of any character(s) in a string. I primarily use it for when I need to check if something exists in a string. Do I have a ‘z’ in my name?\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;z\u0026#39;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNope, so Javascript will return a \u003ccode\u003e-1\u003c/code\u003e. How about a ‘d’?\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;d\u0026#39;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYes, Javascript will return \u003ccode\u003e5\u003c/code\u003e which is the index of the letter \u003ccode\u003e‘d’\u003c/code\u003e.\u003c/p\u003e\n\u003ch5 id=\"stringprototypereplace\"\u003e\u003ccode\u003eString.prototype.replace\u003c/code\u003e\u003c/h5\u003e\n\u003cp\u003eThe replace method can replace any character(s) with other character(s). For more complex replacing, look into \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions\"\u003eRegular Expression\u003c/a\u003e and how you can use them in \u003ccode\u003e.replace()\u003c/code\u003e. Replace the first \u003ccode\u003e‘n’\u003c/code\u003e in my name with an \u003ccode\u003e‘m’\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emodifiedName\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ereplace\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;n\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;m\u0026#39;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003emodifiedName\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLogs \u003ccode\u003e\u0026quot;Bramdon\u0026quot;\u003c/code\u003e.\u003c/p\u003e\n\u003ch5 id=\"stringprototypetouppercase\"\u003e\u003ccode\u003eString.prototype.toUpperCase\u003c/code\u003e\u003c/h5\u003e\n\u003cp\u003eThis method returns the string with all the lowercase characters converted to uppercase and can be useful for when you’re checking user input and you don’t want to worry about different cases.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003etoUpperCase\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReturns \u003ccode\u003e\u0026quot;BRANDON\u0026quot;\u003c/code\u003e.\u003c/p\u003e\n\u003ch5 id=\"stringprototypetolowercase\"\u003e\u003ccode\u003eString.prototype.toLowerCase()\u003c/code\u003e\u003c/h5\u003e\n\u003cp\u003eSame as above but instead of converting lowercase to uppercase, it converts uppercase to lowercase.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003etoLowerCase\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReturns \u003ccode\u003e\u0026quot;brandon\u0026quot;\u003c/code\u003e.\u003c/p\u003e\n\u003ch4 id=\"a-couple-useful-escape-secquences\"\u003eA couple useful escape secquences\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003e\\n\u003c/code\u003e for newline.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e\\t\u003c/code\u003e for tab character.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eYou can also use escape sequnces if you want to add \u003ccode\u003e“”\u003c/code\u003e or \u003ccode\u003e‘’\u003c/code\u003e to your strings.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003egreeting\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Hello \\\u0026#34;Brandon\\\u0026#34;\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003egreeting\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReturns \u003ccode\u003e\u0026quot;Hello \u0026quot;Brandon\u0026quot;\u0026quot;\u003c/code\u003e.\u003c/p\u003e\n\u003ch3 id=\"number\"\u003eNumber\u003c/h3\u003e\n\u003cp\u003eAny number between -(2^53 – 1) and (2^53 – 1).\u003c/p\u003e\n\u003ch4 id=\"number-methods\"\u003eNumber Methods\u003c/h4\u003e\n\u003cp\u003eNumber methods are useful when trying to represent complex numbers.\u003c/p\u003e\n\u003ch5 id=\"numberprototypetoexponential\"\u003e\u003ccode\u003eNumber.prototype.toExponential()\u003c/code\u003e\u003c/h5\u003e\n\u003cp\u003eReturns a string representing a number in exponential notation.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e77.1234\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003etoExponential\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReturns \u003ccode\u003e\u0026quot;7.71e+1\u0026quot;\u003c/code\u003e.\u003c/p\u003e\n\u003ch5 id=\"numberprototypetofixed\"\u003e\u003ccode\u003eNumber.prototype.toFixed()\u003c/code\u003e\u003c/h5\u003e\n\u003cp\u003eReturns a string representing a number fixed to x amount of decimal places.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e12345.6789\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003etoFixed\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReturns \u003ccode\u003e\u0026quot;12345.7\u0026quot;\u003c/code\u003e.\u003c/p\u003e\n\u003ch5 id=\"numberprototypetoprecision\"\u003e\u003ccode\u003eNumber.prototype.toPrecision();\u003c/code\u003e\u003c/h5\u003e\n\u003cp\u003eReturns a string representing a number using x amount of significant figures.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e5.123456\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003etoPrecision\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReturns \u003ccode\u003e\u0026quot;5.1\u0026quot;\u003c/code\u003e.\u003c/p\u003e\n\u003ch4 id=\"math-propertiesmethods\"\u003eMath properties/methods\u003c/h4\u003e\n\u003cp\u003eIn Javascript there is a Math object which contains many properties and methods which are useful for mathmatical calculations.\u003c/p\u003e\n\u003ch5 id=\"return-eulers-constant\"\u003eReturn Euler’s constant\u003c/h5\u003e\n\u003cp\u003e\u003ccode\u003eMath.E\u003c/code\u003e which returns ~2.718.\u003c/p\u003e\n\u003ch5 id=\"return-the-natural-log-of-x\"\u003eReturn the natural log of x\u003c/h5\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMath.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch5 id=\"rise-x-to-the-y-power\"\u003eRise x to the y power\u003c/h5\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMath.\u003cspan style=\"color:#a6e22e\"\u003epow\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e,\u003cspan style=\"color:#a6e22e\"\u003ey\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch5 id=\"return-a-psuedo-random-number-01\"\u003eReturn a psuedo random number [0,1)\u003c/h5\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMath.\u003cspan style=\"color:#a6e22e\"\u003erandom\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch5 id=\"round-x-to-the-nearest-integer\"\u003eRound x to the nearest integer\u003c/h5\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eMath.\u003cspan style=\"color:#a6e22e\"\u003eround\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ex\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"boolean\"\u003eBoolean\u003c/h3\u003e\n\u003cp\u003eBooleans are either \u003ccode\u003etrue\u003c/code\u003e or \u003ccode\u003efalse\u003c/code\u003e and are typically used in conditional statements. You can either create them explicitly\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ealive\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eor by evaluating a \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators\"\u003ecomparison\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edead\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eisAlive\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e!\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003edead\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eisAlive equals \u003ccode\u003etrue\u003c/code\u003e.\u003c/p\u003e\n\u003ch3 id=\"array\"\u003eArray\u003c/h3\u003e\n\u003cp\u003eAn array is a list of items. In Javascript these items can be any data type, even arrays themselves.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emixedBag\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;sword\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e24\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e, [Math.\u003cspan style=\"color:#a6e22e\"\u003ePI\u003c/span\u003e, Math.\u003cspan style=\"color:#a6e22e\"\u003eE\u003c/span\u003e], \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;shield\u0026#39;\u003c/span\u003e];\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo access an item in an array use [] notation with an index as mentioned over at strings.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;sword\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;shield\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;helmet\u0026#39;\u003c/span\u003e][\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e];\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReturns \u003ccode\u003e'shield'\u003c/code\u003e. In order to figure out how many items are in the array:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003einventory\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;boots\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;gloves\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;pants\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;shirt\u0026#39;\u003c/span\u003e];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003einventoryAmt\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003einventory\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elength\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003einventoryAmt is \u003ccode\u003e4\u003c/code\u003e since there are 4 items in inventory.\u003c/p\u003e\n\u003ch4 id=\"array-methods\"\u003eArray Methods\u003c/h4\u003e\n\u003ch5 id=\"arrayprototypepush\"\u003e\u003ccode\u003eArray.prototype.push()\u003c/code\u003e\u003c/h5\u003e\n\u003cp\u003eAdds whatever is inside the parenthesis to the end of the array. Great for adding items to a list. For example, test scores.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e92\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e95\u003c/span\u003e].\u003cspan style=\"color:#a6e22e\"\u003epush\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReturns \u003ccode\u003e[100,92,95,80]\u003c/code\u003e.\u003c/p\u003e\n\u003ch5 id=\"arrayprototypereverse\"\u003e\u003ccode\u003eArray.prototype.reverse()\u003c/code\u003e\u003c/h5\u003e\n\u003cp\u003eReverses the order of all the items in the array.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e].\u003cspan style=\"color:#a6e22e\"\u003ereverse\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReturns \u003ccode\u003e[3,2,1]\u003c/code\u003e.\u003c/p\u003e\n\u003ch5 id=\"arrayprototypeconcat\"\u003e\u003ccode\u003eArray.prototype.concat()\u003c/code\u003e\u003c/h5\u003e\n\u003cp\u003eCombines two arrays, putting the items from the array in the parenthesis to the end of the main array. This method is a lot faster than grabbing each item by their index and adding them using the \u003ccode\u003e.push()\u003c/code\u003e method.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;a\u0026#39;\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;b\u0026#39;\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;c\u0026#39;\u003c/span\u003e].\u003cspan style=\"color:#a6e22e\"\u003econcat\u003c/span\u003e([\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e]);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReturns \u003ccode\u003e['a','b','c',1,2,3]\u003c/code\u003e.\u003c/p\u003e\n\u003ch5 id=\"arrayprototypejoin\"\u003e\u003ccode\u003eArray.prototype.join()\u003c/code\u003e\u003c/h5\u003e\n\u003cp\u003eConverts the array into a string, with each item seperated by whatever is in the parenthesis. Useful for telling the user the items in their inventory, for example.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003einventory\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e [\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;books\u0026#39;\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;apples\u0026#39;\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;pencils\u0026#39;\u003c/span\u003e];\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;You have \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003einventory\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ejoin\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;, \u0026#34;\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; in your inventory.\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLogs \u003ccode\u003e\u0026quot;You have books, apples, pencils in your inventory.\u0026quot;\u003c/code\u003e\u003c/p\u003e\n\u003ch5 id=\"arrayprototypeindexof\"\u003e\u003ccode\u003eArray.prototype.indexOf()\u003c/code\u003e\u003c/h5\u003e\n\u003cp\u003eSimilar to String.prototype.indexOf(), it returns the index of the item inside the parenthesis.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;chicken\u0026#39;\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;pizza\u0026#39;\u003c/span\u003e,\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;tacos\u0026#39;\u003c/span\u003e].\u003cspan style=\"color:#a6e22e\"\u003eindexOf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;tacos\u0026#39;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eReturns \u003ccode\u003e2\u003c/code\u003e.\u003c/p\u003e\n\u003ch3 id=\"objects\"\u003eObjects\u003c/h3\u003e\n\u003cp\u003eObjects are like arrays, however they’re easier for establishing the relationship between properties and their values. You can store any data type as a property of an object.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eplayer\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e {};\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eplayer\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Brandon\u0026#34;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eplayer\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ehealth\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Number.\u003cspan style=\"color:#a6e22e\"\u003ePOSITIVE_INFINITY\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003econsole\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elog\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eplayer\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; has \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eplayer\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ehealth\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34; health.\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLogs \u003ccode\u003e\u0026quot;Brandon has Infinity health\u0026quot;\u003c/code\u003e. Yup that sounds about right.\u003c/p\u003e\n\u003ch3 id=\"conclusion\"\u003eConclusion\u003c/h3\u003e\n\u003cp\u003eAll of the different data types in Javascript are tools for you to get the job done. When assigning a variable, think to yourself which tool you should use. I had fun doing this lecture. We finished earlier than expected, due to my extra preparations. (Still shuddering over my unpreparedness from last week). We had finished so early in fact, that I went ahead and started teaching next week\u0026rsquo;s material. Do not worry though, my lovely readers only get the most structured of lesson materials. So you\u0026rsquo;ll have to wait until next week to hear more. 🙂\u003c/p\u003e\n",
    "date_published": "2015.10.10",
    "tags": ["Web","JS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2015-10-04-html-css-javascript-link-together/",
    "url": "https://brandonrozek.com/blog/2015-10-04-html-css-javascript-link-together/",
    "title": "HTML, CSS, Javascript, and how they all link together",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI\u0026rsquo;ve been teaching a small class on web development recently, and after my first lecture, I\u0026rsquo;ve gained a newfound respect for teachers. Teaching didn\u0026rsquo;t come as naturally to me as I would have imagined. I tried going in prepared: with a few outlines and a few code demos. Instead of letting my preparation go to waste, I decided to share them here with you on my site. It\u0026rsquo;s a nice break from the Animatable posts, so I hope you enjoy!\u003c/p\u003e\n\u003cp\u003eI suggest a couple short readings to check out before the lecture:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"http://www.sitepoint.com/web-foundations/css/\"\u003eWhat is CSS\u003c/a\u003e by Adam Roberts\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://www.w3.org/TR/CSS2/box.html\"\u003eBox Model\u003c/a\u003e by the W3C\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eTake a quick look if you wish. These are mostly to give an idea of what is going to happen over the next few lectures. What is HTML, CSS, and Javascript? We\u0026rsquo;ll look into the different parts that form a webpage and how they all interact with each other.\u003c/p\u003e\n\u003ch2 id=\"html\"\u003eHTML\u003c/h2\u003e\n\u003cp\u003eHTML is where the content of your site lives. It\u0026rsquo;s also the file the server returns to no matter what, the bare-bones of a webpage. This file may contain text, pictures, and/or other media.\u003c/p\u003e\n\u003ch2 id=\"css\"\u003eCSS\u003c/h2\u003e\n\u003cp\u003eThis is where you style your content. Whether it\u0026rsquo;s through colors, layout, or typography, there are plenty of different ways for you to visually manipulate your content.\u003c/p\u003e\n\u003ch2 id=\"javascript\"\u003eJavascript\u003c/h2\u003e\n\u003cp\u003eJavascript is where you add functionality of your site. Validating user input or having dynamic content are one of the many things you can do with Javascipt.\u003c/p\u003e\n\u003ch2 id=\"how-do-they-all-link-together\"\u003eHow do they all link together?\u003c/h2\u003e\n\u003cp\u003eHTML, CSS, and Javascript each do what they do the best. So how can you have them all play nicely on the same ball field?\u003c/p\u003e\n\u003ch3 id=\"in-html\"\u003eIn HTML\u003c/h3\u003e\n\u003cp\u003eLink the CSS file\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003elink\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erel\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;stylesheet\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehref\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;style.css\u0026#39;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eLink the Javascript file\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003escript\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esrc\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;script.js\u0026#39;\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003escript\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eGive the \u003ccode\u003e\u0026lt;p\u0026gt;\u003c/code\u003e tag a class of hello and id of world to use in CSS and Javascript\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003ep\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;hello\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eid\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;world\u0026#39;\u003c/span\u003e\u0026gt;\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ep\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"in-css\"\u003eIn CSS\u003c/h3\u003e\n\u003cp\u003eRefer to any element with \u003ccode\u003eclass='hello'\u003c/code\u003e and change it\u0026rsquo;s text color to red.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003ehello\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003ecolor\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003ered\u003c/span\u003e; }\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAny element with an \u003ccode\u003eid='world'\u003c/code\u003e will have it\u0026rsquo;s font-size changed to 2rem\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e#world { \u003cspan style=\"color:#66d9ef\"\u003efont-size\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e; }\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eYou don\u0026rsquo;t have to only use the class and id attributes in html, you can refer to any attribute. This code snippet grabs any canvas element with \u003ccode\u003edata='sales'\u003c/code\u003e and changes it\u0026rsquo;s border to be 5px thick, dashed, and the color blue.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003ecanvas\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003edata\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003esales\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003eborder\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003epx\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003edashed\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eblue\u003c/span\u003e; }\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"in-javascript\"\u003eIn Javascript\u003c/h3\u003e\n\u003cp\u003eJavascript has many properties and methods you can use to reference different HTML elements To grab (an) element(s)\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocument.\u003cspan style=\"color:#a6e22e\"\u003egetElementById\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocument.\u003cspan style=\"color:#a6e22e\"\u003egetElementsByClassName\u003c/span\u003e(); \u003cspan style=\"color:#75715e\"\u003e//Returns an array of element(s)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003edocument.\u003cspan style=\"color:#a6e22e\"\u003equerySelector\u003c/span\u003e(); \u003cspan style=\"color:#75715e\"\u003e//Returns the first matching selector\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003edocument.\u003cspan style=\"color:#a6e22e\"\u003equerySelectorAll\u003c/span\u003e(); \u003cspan style=\"color:#75715e\"\u003e//Returns an array of element(s)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo add a class to an element\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eelement\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eclassName\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e“\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erandom\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e”\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e//Note the space\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eTo check whether a certain condition is true in the browser\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewindow.\u003cspan style=\"color:#a6e22e\"\u003ematchMedia\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;aspect-ratio: 12/8\u0026#39;\u003c/span\u003e); \u003cspan style=\"color:#75715e\"\u003e//Returns true if the aspect ratio is 12/8\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003ewindow.\u003cspan style=\"color:#a6e22e\"\u003einnerHeight\u003c/span\u003e; \u003cspan style=\"color:#75715e\"\u003e//Is the height of the window\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003ewindow.\u003cspan style=\"color:#a6e22e\"\u003einnerWidth\u003c/span\u003e; \u003cspan style=\"color:#75715e\"\u003e//Is the width of the window\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cem\u003eAnd a lot more\u003c/em\u003e\u003c/p\u003e\n\u003ch2 id=\"what-should-handle-what\"\u003eWhat should handle what?\u003c/h2\u003e\n\u003cp\u003eHTML, CSS, and Javascript should all handle what each of them does best. HTML should handle the content, CSS should handle the styles/presentation, and Javascript should handle the behavior of the webpage. Why, you ask? One reason is that it demonstrates a “separation of concerns”. It would be a mess if you\u0026rsquo;re writing an article in the HTML and you put style attributes all over the place. That would make the markup confusing and hard to read/edit.\u003c/p\u003e\n\u003ch2 id=\"cant-i-just-put-everything-in-a-javascript-file\"\u003eCan\u0026rsquo;t I just put everything in a Javascript file?\u003c/h2\u003e\n\u003cp\u003eYes you can, but then you lose some of the great features of HTML and CSS. HTML and CSS ignore everything that it doesn\u0026rsquo;t understand. When Javascript encounters something it doesn\u0026rsquo;t understand, it stops running the code completely, regardless of what comes after. Please note that I am only speaking about what gets served to the user. When it comes to what\u0026rsquo;s actually on your server, then store your content however you wish.\u003c/p\u003e\n\u003ch2 id=\"conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eTo me personally, my first lecture was a mess. Some of my examples didn\u0026rsquo;t work properly in the first type-through and I was everywhere. Though, to be sharing the little knowledge I do have, comes with its own joy. It must not have gone as terrible as I imagined it in my head because they seemed enthusiastic for the next lecture. So I\u0026rsquo;m excited to share this outline with you, and hopefully I\u0026rsquo;ll have many more to come. Until next time. 🙂\u003c/p\u003e",
    "date_published": "2015.10.04",
    "tags": ["Web"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2015-10-03-animatable-location/",
    "url": "https://brandonrozek.com/blog/2015-10-03-animatable-location/",
    "title": "Animatable: Location",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eAnimating the location of objects is one of the most common types of animation. It\u0026rsquo;s interesting to animate this way because you need to keep in mind how each of the elements interact with each other to constitute a page.\u003c/p\u003e\n\u003cp\u003eThis is part 4 of my series on animation. Check out the other parts of this series!\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003ePart 1 — \u003ca href=\"https://brandonrozek.com/2015/05/animatable-border/\"\u003eAnimatable: Border\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003ePart 2 — \u003ca href=\"https://brandonrozek.com/2015/09/animatable-box-model/\"\u003eAnimatable: Box Model\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003ePart 3 — \u003ca href=\"https://brandonrozek.com/2015/10/animatable-text/\"\u003eAnimatable: Text\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThis post goes along well with this \u003ca href=\"http://codepen.io/brandonrozek/full/NGbWzo/\"\u003eCodepen demo\u003c/a\u003e{.broken_link}, I\u0026rsquo;ll reference it multiple times in this post. Now onto animations!\u003c/p\u003e\n\u003ch3 id=\"background-position\"\u003ebackground-position\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts keywords, percentages, or lengths\u003c/li\u003e\n\u003cli\u003eOffset-x | Offset-Y\u003c/li\u003e\n\u003cli\u003eInitial Value: 0% 0%\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eBackground-position sets where the background is relative to its background-origin. In the Codepen demo (#1), you can see the background image scrolling horizontally.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003ebackground-position\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003ebackground-position\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e; }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003ebackground-position\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-image\u003c/span\u003e: url(\u003cspan style=\"color:#e6db74\"\u003ehttps://upload.wikimedia.org/wikipedia/commons/d/d3/Tajik_mountains_edit.jpg\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e6\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e8\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-size\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e200\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-position\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003ebackground-position\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003elinear\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"position-with-left-right-top-bottom\"\u003eposition with left, right, top, bottom\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts length, percentage, or some keywords\u003c/li\u003e\n\u003cli\u003eInitial value: auto\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThe left, right, top, and bottom properties require the position to be set to something other than static. When you add a value to any of these properties just imagine that value pushing the element on that side. For example: for \u003ccode\u003eleft: 2rem\u003c/code\u003e imagine that the element is being pushed on the left side by 2rem, making it move to the right 2rem. In the demo (#2), the box is being pushed in a square path.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003eposition\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e25\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e%\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003etop\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eleft\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e50\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e%\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003etop\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eleft\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e75\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e%\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003etop\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eleft\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eposition\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eposition\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003erelative\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003etop\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eleft\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-color\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003elightblue\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eposition\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1.5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"vertical-align\"\u003evertical-align\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts keywords, percentages, or lengths (Negative values allowed)\u003c/li\u003e\n\u003cli\u003eInitial Value: baseline\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eVertical-align sets how vertically the inline element or text is compared to the baseline. In the Codepen demo (#3), the question has its vertical align being manipulated which causes the “Maybe” to bounce up and down.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003evertical-align\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003evertical-align\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e; }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003evertical-align\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003efont-size\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1.5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003evertical-align\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003evertical\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ealign \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease-out\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"z-index\"\u003ez-index\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts any integer value\u003c/li\u003e\n\u003cli\u003eInitial Value: auto\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIf elements overlap, z-index determines which element appears on top. If the z-index is the same, then it is controlled by source order. In the demo (#4), The z-index of the biggest box changes, revealing what is under it.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003ez-index\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003ez-index\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003ez-index\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eposition\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eabsolute\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eleft\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003edisplay\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003einline-block\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003ez-index\u003c/span\u003e:\u003cspan style=\"color:#a6e22e\"\u003enth-child\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-color\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003elightgreen\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ez-index\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003ez-index\u003c/span\u003e:\u003cspan style=\"color:#a6e22e\"\u003enth-child\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-color\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e#F4FFA4\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ez-index\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003ez-index\u003c/span\u003e:\u003cspan style=\"color:#a6e22e\"\u003enth-child\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-color\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e#DEB0ED\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ez-index\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003ez-index\u003c/span\u003e:\u003cspan style=\"color:#a6e22e\"\u003enth-child\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e4\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-color\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e#D8EADF\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ez-index\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003ez-index\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"conclusion\"\u003eConclusion\u003c/h3\u003e\n\u003cp\u003eBy animating the location of an element, it opens up a whole bunch of different opportunities. Using motion, you can signify that the user has indeed pressed the button, instead of what would otherwise leave them there clicking on the button multiple times thinking that nothing has happened. Using motion, you can bring a webpage that would\u0026rsquo;ve otherwise been boring to life. Use these animations to work, and I\u0026rsquo;ll be back with another animatable post soon!\u003c/p\u003e\n\u003ch4 id=\"the-links\"\u003eThe links\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/background-position\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/background-position\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/left\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/left\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/vertical-align\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/vertical-align\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/z-index\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/z-index\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.webplatform.org/wiki/css/properties/background-position\"\u003ehttps://docs.webplatform.org/wiki/css/properties/background-position\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.webplatform.org/wiki/css/properties/left\"\u003ehttps://docs.webplatform.org/wiki/css/properties/left\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.webplatform.org/wiki/css/properties/vertical-align\"\u003ehttps://docs.webplatform.org/wiki/css/properties/vertical-align\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.webplatform.org/wiki/css/properties/z-index\"\u003ehttps://docs.webplatform.org/wiki/css/properties/z-index\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e",
    "date_published": "2015.10.03",
    "tags": ["Web","CSS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2015-10-03-animatable-text/",
    "url": "https://brandonrozek.com/blog/2015-10-03-animatable-text/",
    "title": "Animatable: Text",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis post is part 3 of my series on animation. In this post, I\u0026rsquo;ll show you different animations you can add onto text. If you haven\u0026rsquo;t already, you should check out \u003ca href=\"https://brandonrozek.com/2015/09/animatable-box-model/\"\u003epart 1\u003c/a\u003e and \u003ca href=\"https://brandonrozek.com/2015/05/animatable-border/\"\u003epart 2\u003c/a\u003e of this series. Animations on text can be used to bring attention, to add importance, or to convey a point. As with all animations, however, keep your user in mind and your text readable.\u003c/p\u003e\n\u003cp\u003eThis post follows along with a \u003ca href=\"http://codepen.io/brandonrozek/full/dYGwbE/\"\u003eCodepen demo\u003c/a\u003e{.broken_link} I made.\u003c/p\u003e\n\u003ch3 id=\"line-height\"\u003eline-height\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts certain keywords, or any positive number or length\u003c/li\u003e\n\u003cli\u003eInitial value: normal\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eLine-height is the space between each line in a text block. It is commonly recommended that you use a unitless line-height because then it takes the font-size into consideration. When you use an unitless value, the browser determines the line-height by taking the unitless value and multiplying it by the element\u0026rsquo;s font-size. In the Codepen demo (#1), you can see the line-height decreasing while the opacity increases.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003eline-height\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eopacity\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eline-height\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1.2\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eline-height\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eopacity\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eline-height\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e2.5\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eline-height\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.75\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.2\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"font-weight\"\u003efont-weight\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts certain keywords or 100, 200, 300, 400, 500, 600, 700, 800, 900 (the higher the number, the darker the font-weight)\u003c/li\u003e\n\u003cli\u003eInitial value: normal\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eFont-weight specifies the boldness of the text. If the typeface doesn\u0026rsquo;t come with multiple weights, then the animation would only happen between the weights that it does have. In the demo (#2), the text will go from normal weight to bold.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003efont-weight\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003efont-weight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e900\u003c/span\u003e;}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003efont-weight\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003efont-weight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003efont-weight\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003elinear\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.2\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"font-size\"\u003efont-size\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts any length\u003c/li\u003e\n\u003cli\u003eInitial value: medium\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIt is important to note that changing the font-size could change the value of other text properties that are dependent upon it. (Like unitless line-heights) In the demo (#3), you can see the text\u0026rsquo;s font-size shrinking.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003efont-size\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003efont-size\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e.1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003efont-size\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003efont-size\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003efont-size\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease-out\u003c/span\u003e  \u003cspan style=\"color:#ae81ff\"\u003e.1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"text-shadow\"\u003etext-shadow\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts a color and 3 lengths\u003c/li\u003e\n\u003cli\u003eColor | Offset-X | Offset-Y | Blur-radius\u003c/li\u003e\n\u003cli\u003eInitial value: none\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eText-shadow applies a shadow to both the text and its text-decoration. Multiple shadows can be added, and they are applied from front to back. In the animation (#4), you can see the text\u0026rsquo;s shadow move.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003etext-shadow\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003etext-shadow\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e25\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003epx\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003epx\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003epx\u003c/span\u003e rgba(\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e.9\u003c/span\u003e);}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003etext-shadow\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003efont-size\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1.5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003etext-shadow\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e-10\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003epx\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003epx\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e3.5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003epx\u003c/span\u003e rgba(\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e.3\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etext\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003eshadow \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"text-decoration-color\"\u003etext-decoration-color\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts a color value\u003c/li\u003e\n\u003cli\u003eInitial value: currentColor\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThis sets the color for \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/text-decoration-line\"\u003etext-decoration-line\u003c/a\u003e (underlines, overlines, or strike-throughs) In the demo (#5), the strike-through changes from red to black.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003etext-decoration-color\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003etext-decoration-color\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eblack\u003c/span\u003e;}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003etext-decoration-color\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003etext-decoration-color\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003ered\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003etext-decoration-line\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eline-through\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etext\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003edecoration-color \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003elinear\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"word-spacing\"\u003eword-spacing\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts keywords or positive/negative length\u003c/li\u003e\n\u003cli\u003eInitial value: normal\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWord-spacing defines the space between tags and words. Negative values bring the words closer to each other. In the demo (#6), you can see the word-spacing increase with ‘good bye!’ where the word ‘bye!’ is moving away.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003eword-spacing\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003eword-spacing\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eword-spacing\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eword-spacing\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003enormal\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eword-spacing\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease-in\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"letter-spacing\"\u003eletter-spacing\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts keywords or positive/negative length\u003c/li\u003e\n\u003cli\u003eInitial value: normal\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eLetter-spacing specifies the spacing between text characters. Negative values bring the letters closer together. In the demo (#7), each letter gets separated from one another.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003eletter-spacing\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003eletter-spacing\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eletter-spacing\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eletter-spacing\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eletter-spacing\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.75\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"conclusion\"\u003eConclusion\u003c/h3\u003e\n\u003cp\u003eThese animations show the different things you can do with text. Perhaps you\u0026rsquo;ll add a small animation to a heading to bring depth and attention, or you\u0026rsquo;ll add some to the text of a button to scream “call to action”. Whatever you decide, I hope this post helped. I\u0026rsquo;ll see you again next time with another animatable post! 🙂\u003c/p\u003e\n\u003ch4 id=\"the-links\"\u003eThe links\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/line-height\"\u003ehttps://docs.webplatform.org/wiki/css/properties/line-height\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/line-height\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/line-height\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.webplatform.org/wiki/css/properties/font-weight\"\u003ehttps://docs.webplatform.org/wiki/css/properties/font-weight\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/font-weight\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.webplatform.org/wiki/css/properties/font-size\"\u003ehttps://docs.webplatform.org/wiki/css/properties/font-size\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/font-size\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/font-size\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.webplatform.org/wiki/css/properties/text-shadow\"\u003ehttps://docs.webplatform.org/wiki/css/properties/text-shadow\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.webplatform.org/wiki/css/properties/text-decoration-color\"\u003ehttps://docs.webplatform.org/wiki/css/properties/text-decoration-color\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/text-decoration-color\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/text-decoration-color\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.webplatform.org/wiki/css/properties/word-spacing\"\u003ehttps://docs.webplatform.org/wiki/css/properties/word-spacing\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/word-spacing\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/word-spacing\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.webplatform.org/wiki/css/properties/letter-spacing\"\u003ehttps://docs.webplatform.org/wiki/css/properties/letter-spacing\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/letter-spacing\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/letter-spacing\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e",
    "date_published": "2015.10.03",
    "tags": ["Web","CSS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2015-09-14-animatable-box-model/",
    "url": "https://brandonrozek.com/blog/2015-09-14-animatable-box-model/",
    "title": "Animatable: Box Model",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis post is part 2 of an animation series I am doing; you can read part 1 \u003ca href=\"https://brandonrozek.com/2015/05/animatable-border/\"\u003ehere\u003c/a\u003e. In this post, we\u0026rsquo;ll look at the different parts of the box model (margin, padding, height, and width) and see how they can be animated.\u003c/p\u003e\n\u003cp\u003eThe W3C has a great starting reference for the \u003ca href=\"http://www.w3.org/TR/CSS2/box.html\"\u003eCSS Box Model\u003c/a\u003e. It can be wordy at times, but has everything you need to know. I had never heard of \u003cem\u003emargin collapsing\u003c/em\u003e until I read that. Luckily there is a great \u003ca href=\"https://css-tricks.com/what-you-should-know-about-collapsing-margins/\"\u003epost\u003c/a\u003e on CSS-Tricks written by Geoff Graham explaining what it is. To see it all in action, take a look at this Codepen \u003ca href=\"http://codepen.io/brandonrozek/full/RWPYgV/\"\u003edemo\u003c/a\u003e{.broken_link}–  I reference this multiple times in this post. Now, on to the box model.\u003c/p\u003e\n\u003ch3 id=\"margin\"\u003eMargin\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts 1 to 4 numerical values (negative numbers are allowed)\u003c/li\u003e\n\u003cli\u003eIf you use 4 values, the first value is the top margin and the rest follow in a clockwise fashion\u003c/li\u003e\n\u003cli\u003eInitial value: 0\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eMargins can be described as the space around an element. In the Codepen demo (#1), it shows 2 boxes. The first box has a margin-right that is increasing, making it seem as though it\u0026rsquo;s pushing the second box away.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003emargin\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003emargin-right\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e7\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e; }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003emargin\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003edisplay\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003einline-block\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-color\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003elightblue\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003evertical-align\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etop\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003emargin\u003c/span\u003e:\u003cspan style=\"color:#a6e22e\"\u003efirst-of-type\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003emargin-right\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003emargin\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"padding\"\u003ePadding\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts 1 to 4 non-negative values\u003c/li\u003e\n\u003cli\u003eIf you use 4 values, the first value is the top margin and the rest follow in a clockwise fashion\u003c/li\u003e\n\u003cli\u003eInitial value: 0\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003ePadding is the space between the content and the border of an element. In the demo (#2),  it shows a box in which its padding is increasing.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003epadding\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003epadding\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e; }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003epadding\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003edisplay\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003einline-block\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003epadding\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-color\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003elightgreen\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003epadding\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2.5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"height\"\u003eHeight\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts a non-negative number, this number is overridden however by (min/max)-height\u003c/li\u003e\n\u003cli\u003eInitial value: auto\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e“Height” is the height of an element without its padding, border, or margin. In the demo (#3) you can see the boxes\u0026rsquo; height shrink, and each box begins its animation at a different time.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003eheight\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e.01\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e; }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheight\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003edisplay\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003einline-block\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-color\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eviolet\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1.25\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003evertical-align\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003etop\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheight\u003c/span\u003e:\u003cspan style=\"color:#a6e22e\"\u003enth-of-type\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003en\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1.25\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.2\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheight\u003c/span\u003e:\u003cspan style=\"color:#a6e22e\"\u003enth-of-type\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e2n\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1.25\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.4\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eheight\u003c/span\u003e:\u003cspan style=\"color:#a6e22e\"\u003enth-of-type\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e3n\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1.25\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.6\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"width\"\u003eWidth\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts a non-negative number, this number is overridden however by (min/max)-width\u003c/li\u003e\n\u003cli\u003eInitial value: auto\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e“Width” is the width of an element without its  padding, border, or margin. In the demo (#4), it is similar to #3, however, its the width being affected as opposed to the height.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003ewidth\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e.01\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e; }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003ewidth\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003emargin-bottom\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e.2\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eheight\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e6.5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ebackground-color\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003ebisque\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003ewidth\u003c/span\u003e:\u003cspan style=\"color:#a6e22e\"\u003enth-of-type\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003en\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1.25\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.2\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003ewidth\u003c/span\u003e:\u003cspan style=\"color:#a6e22e\"\u003enth-of-type\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e2n\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1.25\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.4\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003ewidth\u003c/span\u003e:\u003cspan style=\"color:#a6e22e\"\u003enth-of-type\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e3n\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1.25\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.6\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"conclusion\"\u003eConclusion\u003c/h3\u003e\n\u003cp\u003eAnd so with this we can add another collection of animations to our toolbelt! If you\u0026rsquo;re wondering why I left border out of this box-model post, its because I have already written a post dedicated to just the \u003ca href=\"https://brandonrozek.com/2015/05/animatable-border/\"\u003eborder animation\u003c/a\u003e. Here are some of the resources I looked at for this post. Hopefully I\u0026rsquo;ll come back with another animatable post soon!\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/margin\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/margin\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://web.archive.org/web/20151112043907/https://docs.webplatform.org/wiki/css/properties/margin\"\u003ehttps://web.archive.org/web/20151112043907/https://docs.webplatform.org/wiki/css/properties/margin\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/padding\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/padding\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.webplatform.org/wiki/css/properties/padding\"\u003ehttps://docs.webplatform.org/wiki/css/properties/padding\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/height\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/height\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.webplatform.org/wiki/css/properties/height\"\u003ehttps://docs.webplatform.org/wiki/css/properties/height\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/width\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/width\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://web.archive.org/web/20150919163210/https://docs.webplatform.org/wiki/css/properties/width\"\u003ehttps://web.archive.org/web/20150919163210/https://docs.webplatform.org/wiki/css/properties/width\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e",
    "date_published": "2015.09.14",
    "tags": ["Web","CSS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2015-05-23-animatable-border/",
    "url": "https://brandonrozek.com/blog/2015-05-23-animatable-border/",
    "title": "Animatable: Border",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eThis is part 1 of an animation series I\u0026rsquo;m doing. It is inspired by Lea Verou\u0026rsquo;s talk called “\u003ca href=\"https://www.youtube.com/watch?v=JSaMl2OKjfQ\"\u003eThe Humble Border-Radius.\u003c/a\u003e” I looked at her site and she has a good demo of a bunch of different animations \u003ca href=\"http://lea.verou.me/2011/10/animatable-a-css-transitions-gallery/\"\u003ehere\u003c/a\u003e. My goal here is to create a more comprehensive guide to these different animatable properties–mainly for future reference. Animations play a big part in adding interactivity to the web, so why not explore some possible options?\u003c/p\u003e\n\u003cp\u003eThis post follows well along with my Codepen \u003ca href=\"http://codepen.io/brandonrozek/full/waWMWR/\"\u003edemo\u003c/a\u003e{.broken_link}, where I\u0026rsquo;ll state the box number that applies to what I\u0026rsquo;m talking about. **Initial values must be explicitly stated, implicit initial values are generally ignored in animation** *\\\u003cem\u003e*In English terms, you must have already stated a value for what you are animating before you animate it**\u003c/em\u003e\u003c/p\u003e\n\u003ch3 id=\"border-color\"\u003eBorder-color\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts 1 to 4 color values\u003c/li\u003e\n\u003cli\u003eEach value corresponds to each side of the border (starting from the top and going clockwise)\u003c/li\u003e\n\u003cli\u003eInitial Value: currentColor\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eBorder-color animates by splitting the colors to their red, green and blue components and raises/lowers them to its new value. (\u003ca href=\"http://codepen.io/brandonrozek/pen/PqzPMe\"\u003eDemo\u003c/a\u003e{.broken_link}) (\u003ca href=\"http://www.w3.org/TR/css3-transitions/#animtype-color\"\u003eSpec\u003c/a\u003e) Example of animation corresponds to #1 in the pen, but I will rewrite the relevant code here.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003ecolor\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003eborder-color\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003epurple\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ered\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003egreen\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eblue\u003c/span\u003e; }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eborder-color\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eborder-color\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003ewhite\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003ecolor\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.4\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease-in\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e \u003c/p\u003e\n\u003ch3 id=\"border-radius\"\u003eBorder-radius\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts 1 to 4 values for both horizontal and vertical radii\u003c/li\u003e\n\u003cli\u003eEach value corresponds to a corner starting from the top left and going clockwise\u003c/li\u003e\n\u003cli\u003eInitial Value: 0\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eborder-radius\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eborder-radius\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e40\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e30\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e60\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e50\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e20\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e40\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e60\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e/** is the same as **/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eborder-top-left-radius\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e40\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e20\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eborder-top-right-radius\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e30\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e40\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eborder-bottom-right-radius\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e60\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e60\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eborder-bottom-left-radius\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e50\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e/** where the first value is the horizontal\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e    radius and the second value the vertical radius **/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eFor animation, this corresponds to #2 in the pen I made at the top. I’ll repeat the relevant code here.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003eradius\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003eborder-radius\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e20\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e%\u003c/span\u003e; }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eborder-radius\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eborder-radius\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: radius \u003cspan style=\"color:#ae81ff\"\u003e.5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease-in\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e \u003c/p\u003e\n\u003ch3 id=\"border-width\"\u003eBorder-width\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAccepts 1 to 4 values\u003c/li\u003e\n\u003cli\u003eEach value corresponds to a side of the border (starting from the top and going clockwise)\u003c/li\u003e\n\u003cli\u003eInitial Value: medium\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eFor animation, this corresponds to #3 in the pen I made at the top. I’ll repeat the relevant code here.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003ekeyframes\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003ewidth\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003eto\u003c/span\u003e { \u003cspan style=\"color:#66d9ef\"\u003eborder-width\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e.15\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.25\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.15\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.25\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e; }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\u003cspan style=\"color:#a6e22e\"\u003eborder-width\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eborder-width\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e.7\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003erem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eanimation\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003ewidth\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eease-in\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e.1\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003es\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003einfinite\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ealternate\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e \u003c/p\u003e\n\u003ch3 id=\"conclusion\"\u003eConclusion\u003c/h3\u003e\n\u003cp\u003eAnimations are quite enjoyable. The last box in my Codepen demo tries combining all three of those animations. (Super Wacky!)  You don\u0026rsquo;t need to use keyframe animations to achieve this, you can also use the transition property. I used keyframes so you can better visualize what\u0026rsquo;s going on. There are plenty of other animatable properties to go through, so I\u0026rsquo;ll get started on those. In the meantime, if you want to look at some of the sites I used for research I\u0026rsquo;ll include the links below.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/animation\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/animation\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"http://www.w3.org/TR/css3-transitions/#animatable-css\"\u003ehttp://www.w3.org/TR/css3-transitions/#animatable-css\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/border-color\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/border-color\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/border-radius\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/border-width\"\u003ehttps://developer.mozilla.org/en-US/docs/Web/CSS/border-width\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.webplatform.org/wiki/css/properties/border-color\"\u003ehttps://docs.webplatform.org/wiki/css/properties/border-color\u003c/a\u003e{.broken_link}\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.webplatform.org/wiki/css/properties/border-radius\"\u003ehttps://docs.webplatform.org/wiki/css/properties/border-radius\u003c/a\u003e{.broken_link}\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.webplatform.org/wiki/css/properties/border-width\"\u003ehttps://docs.webplatform.org/wiki/css/properties/border-width\u003c/a\u003e{.broken_link}\u003c/li\u003e\n\u003c/ul\u003e",
    "date_published": "2015.05.23",
    "tags": ["Web","CSS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2015-04-16-responsive-layout-and-animation/",
    "url": "https://brandonrozek.com/blog/2015-04-16-responsive-layout-and-animation/",
    "title": "Responsive Layout and Animation",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003eI saw \u003ca href=\"http://madebymike.com.au/\"\u003eMike Riethmuller’s\u003c/a\u003e precision typography \u003ca href=\"http://codepen.io/MadeByMike/pen/YPJJYv\"\u003epen\u003c/a\u003e{.broken_link}, and was highly impressed. I think the equation used has other purposes as well\u003c/p\u003e\n\u003cp\u003eSide Note: I changed the form of the equation to something similar to y = mx + b so that I can more easily recognize how it functions\u003c/p\u003e\n\u003ch4 id=\"responsive-layout\"\u003eResponsive Layout\u003c/h4\u003e\n\u003cp\u003eThere are many occasions where I want an element on the page to move between two points. The navigation in the header of my site (at the time of writing) is a great example of this. So knowing the two points I want it to lie between and having the screen width as the variable, I can plug in:\u003c/p\u003e\n\u003cp\u003e$$$\nf(x) = (100 * (b - a)/(d - c))X + (ad - bc) / (d - c)\n$$$\u003c/p\u003e\n\u003cp\u003ewhere a is the start pixel b is the end pixel c is the start media query d is the end media query and X is the screen width out of 100 otherwise known as 1vw **Don\u0026rsquo;t forget to keep track of your units!! Whether it\u0026rsquo;s px/rem/em/etc.** Say I want to push a box towards the right a minimum of 5px, a maximum of 20px and for the push to vary between the widths 400-800px. Then I would write…\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003emedia\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003emin-width\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e400px\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003eand\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003emax-width\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e800px\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .\u003cspan style=\"color:#a6e22e\"\u003ebox\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eposition\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003erelative\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eleft\u003c/span\u003e: calc(\u003cspan style=\"color:#ae81ff\"\u003e3.75\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003evw\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003epx\u003c/span\u003e) \u003cspan style=\"color:#75715e\"\u003e/*After simplifying the equation*/\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThat would only make it vary between 400-800px. Now we need to include what happens under 400px and over 800px.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003emedia\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003emax-width\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e400px\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .\u003cspan style=\"color:#a6e22e\"\u003ebox\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eposition\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003erelative\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eleft\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003epx\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003emedia\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003emin-width\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e400px\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003eand\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003emax-width\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e800px\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .\u003cspan style=\"color:#a6e22e\"\u003ebox\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eposition\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003erelative\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eleft\u003c/span\u003e: calc(\u003cspan style=\"color:#ae81ff\"\u003e3.75\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003evw\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003epx\u003c/span\u003e); \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e@\u003cspan style=\"color:#66d9ef\"\u003emedia\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003emin-width\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e800px\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .\u003cspan style=\"color:#a6e22e\"\u003ebox\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eposition\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003erelative\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eleft\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e20\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003epx\u003c/span\u003e; \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis is exactly like Mike\u0026rsquo;s pen, but instead he uses the equation to adjust the font-size between an upper and lower bound. You can apply this method to anything that accepts calc() and viewport units. Here is my \u003ca href=\"http://codepen.io/brandonrozek/pen/JoQVEb\"\u003epen\u003c/a\u003e{.broken_link} showing some use cases. To make your life easier, I made a quick little tool where you can input the variables and it will provide you with a simpler form of the equation to put into your calc() function \u003ca href=\"http://codepen.io/brandonrozek/pen/KpPwGL\"\u003ehere\u003c/a\u003e{.broken_link}.\u003c/p\u003e\n\u003ch4 id=\"animation\"\u003eAnimation\u003c/h4\u003e\n\u003cp\u003eThis is where the majority of my research went towards. It\u0026rsquo;s not as practical as say positioning an element is but I find it interesting. Like, what if I can manipulate the acceleration of the function?\u003c/p\u003e\n\u003cp\u003e$$$\nf(x) = ((b - a) / (d^n - c^n))X^n + (ad^n - bc^n) / (d^n - c^n)\u003c/p\u003e\n\u003cp\u003e$$$\u003c/p\u003e\n\u003cp\u003eWhere a is the start unit b is the end unit c is the start time d is the end time n is the acceleration modifier and X is time The interesting part of the function here is the n. If I keep n at 1, then the acceleration is constant. If it\u0026rsquo;s less than one, then it\u0026rsquo;s fast in the beginning and slows down at the end. If it\u0026rsquo;s greater than one, then it\u0026rsquo;s the opposite. I also made a little pen \u003ca href=\"http://codepen.io/brandonrozek/pen/RNzdOV\"\u003ehere\u003c/a\u003e to demo this for you.\u003c/p\u003e\n\u003ch4 id=\"conclusion\"\u003eConclusion\u003c/h4\u003e\n\u003cp\u003eHaving a function that goes between two points is incredibly handy. Now when it comes to positioning, I don\u0026rsquo;t have to guess which values match the design. If i cant something to be between here and there fluidly, I can do it. What about animation? Chaining them together should have an interesting affect… P.S For those of you crazy people who like to see the theory behind the math, (like myself) I have my scanned work \u003ca href=\"/blog/2015-04-16-function-two-points-theory/\"\u003ehere\u003c/a\u003e.\u003c/p\u003e",
    "date_published": "2015.04.16",
    "tags": ["Web","CSS"],
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        ,
            {
    "id": "https://brandonrozek.com/blog/2015-04-16-function-two-points-theory/",
    "url": "https://brandonrozek.com/blog/2015-04-16-function-two-points-theory/",
    "title": "Math Theory for Function Between Two Points",
    "authors": [
        
            { "name": "Brandon Rozek" }
        
    ],
    "content_html": "\u003cp\u003e\u003cimg src=\"/files/images/blog/04162015_1.jpeg\" alt=\"\"\u003e\n\u003cimg src=\"/files/images/blog/04162015_2.jpeg\" alt=\"\"\u003e\n\u003cimg src=\"/files/images/blog/04162015_3.jpeg\" alt=\"\"\u003e\n\u003cimg src=\"/files/images/blog/04162015_4.jpeg\" alt=\"\"\u003e\u003c/p\u003e",
    "date_published": "2015.04.16",
    "tags": null,
    "_syndication": {
        "mastodon": {
            "enabled": false,
            "toot_id": null,
            "toot_text": ""
        },
        "medium": {
            "enabled": false,
            "post_id": null
        }
    }
}
        
    ]
}

