<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>rspk.org/weblog/rss.xml</title>
        <link>https://rspk.org</link>
        <description>rspk's weblog feed</description>
        <lastBuildDate>Thu, 16 Apr 2026 07:48:05 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>Feed for Node.js</generator>
        <item>
            <title><![CDATA[Book review: Expert C Programming by Peter van der Linden (1994)]]></title>
            <link>https://rspk.org/weblog/expert-c-programming</link>
            <guid>https://rspk.org/weblog/expert-c-programming</guid>
            <pubDate>Thu, 15 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Book review: Expert C Programming by Peter van der Linden (1994)]]></description>
            <content:encoded><![CDATA[
I picked up this book after I saw it in the recommended readings in the [OSTEP](https://pages.cs.wisc.edu/~remzi/OSTEP/) book. Despite the fact that it's over 30 years old now, it is a very illuminating book on C's pitfalls, quirks, and its history.

This book is definitely not meant to be one's first book on C. If you want to learn C today, you should definitely pick something that teaches you modern C (C99+). If you want to learn about C's origins and motivations, the next book should be K&R. Expert C Programming fits nicely as a third book after that because it was written after C was standardized but back when there was still a lot of K&R C in use, and it frequently compares how things work between these versions.

A lot of C code that the book contains may not be very useful anymore and many Unix commands are obsolete now. Still the concepts are very much evergreen. It's hard to claim expertise in C without understanding how OS, CPU, and system calls interact and this book touches on many such aspects.

The text frequently compares how things are done in C versus in other popular systems languages at the time&mdash;Ada and Pascal in particular, and sometimes Fortran and Algol. This makes it very interesting to see how C was different from these languages and why it stood the test of time making it one of the most influential languages and popular in systems programming even today. There is even a chapter on C++ highlighting the fundamentals of object-oriented programming.

By far the most interesting parts of the book are the "Light Relief" sections at the end of each chapter. These sections include some amusing historical incidents and war stories like when GCC took "implementation-defined" too literally, CMU's coke machine, the "phantom finger", and Emacs psychiatrist AI. The text is written in the author's tongue-in-cheek humor style and is filled with his personal anecdotes from working at the Sun compiler team. I'd recommend reading this book solely for these sections if not for C itself.

If you enjoy language history and systems programming, this book is definitely worth your time.

]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Chess set dimensions: official board and piece size standards]]></title>
            <link>https://rspk.org/weblog/chessboard</link>
            <guid>https://rspk.org/weblog/chessboard</guid>
            <pubDate>Sun, 19 Jan 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Chess set dimensions: official board and piece size standards]]></description>
            <content:encoded><![CDATA[
I was trying to make my own chessboard recently. Frustratingly, the recommended measurements/plans seem to be all over the place on the web. This is my attempt to put everything I've learned in one place.

FIDE recommends the following measurements for their official tournament board sets.

## Piece sizes

The diameter of the piece's base should measure **40-50% of its height**. These dimensions may differ by up to 10%, but the order must be kept.

| Piece  | Height (cm)  | Diameter of base |
|--------|--------------|------------------|
| King   | 9.5 (100%)   | 3.8 - 4.75       |
| Queen  | 8.5 (89.47%) | 3.4 - 4.25       |
| Bishop | 7.0 (73.68%) | 2.8 - 3.5        |
| Knight | 6.0 (63.16%) | 2.4 - 3.0        |
| Rook   | 5.5 (57.89%) | 2.2 - 2.75       |
| Pawn   | 5.0 (52.63%) | 2.0 - 2.5        |

(Percentages in parentheses show the height relative to the King for proportional reference. These are not in the standards.)

## Square size

The side of the square should measure **5-6 cm**.

In other words, the square side should be **at least 2x** the diameter of a pawn’s base (four upright pawns placed flat on their bases must fit on one square without overlap).

**Note**: Some sites suggest that the King's base diameter should be **75-80%** of the square's side. There is no mention of this rule in the FIDE rulebook but it does check out in the above recommendations.

## Table size

- The length of the table is **110 cm (&plusmn;15%)**.
- The width is **85 cm** (at least **15 cm** gap between the edge of the table and the board for each player). This allows the overall board size to be up to **85 - 2 x 15 = 55 cm**.
- The height of the table is **74 cm**.

## USCF

USCF uses **inches** instead of metric units to define their recommended dimensions. These values don't differ by a lot when converted to centimeters but aim to be nice rounded values intended for US board makers.

- The King is **3.75"** tall with **1.5"** base. Pieces usually take **75-80%** of the square.
- The square is **2 - 2.25"** long. The **four-pawns test** should hold.
- The board is **20 - 22"** long.

## Links

- [Relevant section of the FIDE handbook](https://handbook.fide.com/chapter/StandardsOfChessEquipment2022)
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Remembering the 50 US states]]></title>
            <link>https://rspk.org/weblog/us-states</link>
            <guid>https://rspk.org/weblog/us-states</guid>
            <pubDate>Mon, 02 Sep 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Remembering the 50 US states]]></description>
            <content:encoded><![CDATA[
In this document, I will explain my system for memorizing the 50 US states. This system relies on mnemonics created using the geographic locations and features of the states. Not only does it make it easy to recall the names, but also to locate them on the map with little effort.

As you will notice, some mnemonics are kinda out there but the stupider the mnemonic, the better it sticks.

## US map

I recommend you open this map in a new window and read this article side by side to see how this all makes sense.

![US states](/img/weblog/us-states.svg)

## The mnemonic

> **WO IM WyNe. CANTO NUCK MIMAL WIIMOP New KTMAG VaN MaRCoNi DM**

| Mnemonic       | States                                                                                              | Meaning                                                                                                                              |
|----------------|-----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|
| WO IM WyNe     | Washington, Oregon, Idaho, Montana, Wyoming, Nebraska                                               | I was a human. Suddenly I'm WyNe (wine). Weird!                                                                                      |
| CANTO NUCK     | California, Arizona, New Mexico, Texas, Oklahoma / Nevada, Utah, Colorado, Kansas                   | I just memorize it but you could remember it as a name like **Cant O'Nuck**.                                                         |
| MIMAL          | Minnesota, Iowa, Missouri, Arkansas, Louisiana                                                      | Vertical wall of 5 states in central US that collectively look like a human. I imagine it as a chef whose name is MIMAL. Chef MIMAL. |
| WIIMOP New     | Wisconsin, Illinois, Indiana, Michigan, Ohio, Pennsylvania, New York                                | This group of states surround the Great Lakes. So it's wet. You'll need a NEW kind of mop for it called a WIIMOP.                    |
| KTMAG          | Kentucky, Tennessee, Mississippi, Alabama, Georgia                                                  | Name of a magazine.                                                                                                                  |
| VaN MaRCoNi DM | Vermont, New Hampshire / Massachusetts, Rhode Island, Connecticut, New Jersey / Delaware, Maryland  | The smaller states at the top left in clockwise direction. Ignore the vowels and just use the consonant letters.                   |

The mnemonic covers 40 states. The remaining 10 are just too easy to need a mnemonic for:

| States            | Description                                                                                                 |
|-------------------|-------------------------------------------------------------------------------------------------------------|
| The Dakotas       | North Dakota, South Dakota                                                                                  |
| The Carolinas     | North Carolina, South Carolina                                                                              |
| The Virginias     | Virginia, West Virginia                                                                                     |
| Maine             | Topmost state of the US east coast.                                                                         |
| Florida           | Pretty distinct protrusion toward the gulf of Mexico.                                                       |
| Alaska and Hawaii | The only 2 non-contiguous US states. Alaska is big and borders Canada. Hawaii is a bunch of little islands. |

## Individual features of states

Here are some geographic features of some states to help them locate on the map:

| State         | Features                                                       |
|---------------|----------------------------------------------------------------|
| Idaho         | Looks like an **i**                                            |
| Montana       | **Mountain** (located on top)                                  |
| Oklahoma      | Looks like a meat cleaver with handle made of **Oak**          |
| Minnesota     | **Mini** mouse on chef MIMAL's head (the rat from Ratatouille) |
| Iowa          | Location of the **eyes (/aɪ/)** of chef MIMAL                  |
| Missouri      | Aren't you **missing** Chef MIMAL's heart?                     |
| Arkansas      | Chef MIMAL's **arse** is located here                          |
| Louisiana     | **Legs** of Chef MIMAL                                         |
| Illinois      | Coughing (**ill**) person because of the germs from chef MIMAL's **nose** |
| Indiana       | Illinois is poking **INto** Indiana                            |
| Michigan      | Only state split into two large segments                       |
| New York      | Looks like a **Y**                                             |
| Tennessee     | Denominator of the fraction (Kentucky over **10essee**)        |
| Delaware      | Be **aware**. It's so small it might fall off into the ocean!  |
| Connecticut   | **Connects** Rhode Island and New York                         |
| Rhode Island  | Islands are usually tiny                                       |
| Massachusetts | Looks like a **massive** fishing hook                         |
| New Hampshire | Looks like an **h**                                            |
| Vermont       | Looks like a **V**                                             |
| Maine         | **Main** part of US east coast                                 |
| Florida       | [Florida man](https://en.wikipedia.org/wiki/Florida_Man)'s penis |
| Alaska        | Looks like the **lowercase a**                                 |

## An alternative

If you just care about the names of states without their locations, there is an alternative mnemonic:

> **8MN 4WIA 3CO 2SKTV DR HP GULF**

There are 8 states starting with the letters M and N; 4 with W, I, and A; 3 with C and O; and 2 with S, K, T, and V. There are 8 states that do not share their initial letter with any other states: D, R, H, P, G, U, L, and F.

Some tricks:
- M: 4 start with Mi, 3 start with Ma, and 1 starts with Mo.
- N: 4 start with New, 2 with North and the other two start with Ne.
- Both Ss start with South.

## Credits

I consulted various resources online and took parts of them to come up with this system:

- [Memorize ALL 50 US States (School of Memory Ep. 1) - Nelson Dellis](https://www.youtube.com/watch?v=P0hKV-nASpE)
- [Learn the 50 states with Ms. Alexander](https://web.archive.org/web/20240124021051/https://www.youtube.com/watch?v=dcR4DwFNkPg)
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Multiple ways to implement "Scroll to top"]]></title>
            <link>https://rspk.org/weblog/scroll-top</link>
            <guid>https://rspk.org/weblog/scroll-top</guid>
            <pubDate>Sat, 17 Jun 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Multiple ways to implement "Scroll to top"]]></description>
            <content:encoded><![CDATA[
There are two primary ways to implement a "Scroll to top" button on webpages that I know of:

## Using a link tag

Used by websites like Wikipedia and React docs, this solution is perhaps the simplest, as it doesn't require any JavaScript. Just link to "#" from a link tag in the HTML and all modern browsers will navigate the user to the top of the current page, much like what pressing `Home` does.

This solution does have a downside though. If you want to add smooth scrolling, you have to enable it in your global CSS which may not always be desirable.

```html
<a href="#">Back to top</a>
```

## Using `window.scrollTo()`

This JavaScript method has more flexibility. You can add a click event listener on a button and call [`window.scrollTo`](https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollTo). Set the `behaviour` option to `smooth` to enable smooth scrolling or `auto` for instant navigation:

```javascript
const scrollTopBtn = document.querySelector('#scroll-to-top')

scrollTopBtn.addEventListener('click', () => {
  window.scrollTo(
    top: 0,
    behaviour: 'smooth',
  )
})
```
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Adding share buttons on blog posts]]></title>
            <link>https://rspk.org/weblog/share-buttons</link>
            <guid>https://rspk.org/weblog/share-buttons</guid>
            <pubDate>Tue, 04 Apr 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Adding share buttons on blog posts]]></description>
            <content:encoded><![CDATA[
Rather than relying on pre-built widgets or SDKs from platforms like [Twitter](https://publish.twitter.com/?buttonType=TweetButton&widget=Button), there is a simpler approach to adding "Share on &lt;site&gt;" links to your blog posts using URL redirection. 

Most sites have a dedicated "submit" link that allows for specific query parameters to be used in order to pre-populate the URL and title. The parameter names may differ for each site, for example, for Twitter it would be `https://twitter.com/intent/tweet?text=some text&url=https://example.org`.

You can store these values for different sites in an array of objects and map through it to display a link with the `target` attribute set to `_blank`. These links can then be styled with CSS without loading any external scripts.

```javascript
const shareSites = [
  {
    name: 'Hacker News',
    submitUrl: 'https://news.ycombinator.com/submitlink',
    titleParam: 't',
    urlParam: 'u',
  },
  {
    name: 'Lobsters',
    submitUrl: 'https://lobste.rs/stories/new',
    titleParam: 'title',
    urlParam: 'url',
  },
  {
    name: 'Reddit',
    submitUrl: 'https://reddit.com/submit',
    titleParam: 'title',
    urlParam: 'url',
  },
  {
    name: 'Twitter',
    submitUrl: 'https://twitter.com/intent/tweet',
    titleParam: 'text',
    urlParam: 'url',
  },
]

// Later in the post template component
const title = "How to ..."
const permalink = "https://example.com/..."
...
<div className="share-buttons">
  Share this post on:
  {shareSites.map((site) => (
    <span key={site.name}>
      <a
        href={`${site.submitUrl}
        ?${site.titleParam}=${encodeURIComponent(title)}
        &${site.urlParam}=${encodeURIComponent(permalink)}`}
        target="_blank"
        rel="noopener noreferrer"
      >
        {site.name}
      </a>{" "}
    </span>
  ))}
</div>
```

]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Sorting YouTube playlists from the command line]]></title>
            <link>https://rspk.org/weblog/youtube-playlist-sort</link>
            <guid>https://rspk.org/weblog/youtube-playlist-sort</guid>
            <pubDate>Thu, 05 Jan 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Sorting YouTube playlists from the command line]]></description>
            <content:encoded><![CDATA[
A while ago, I published [plsort](https://plsort.netlify.app), a web app that enables users to sort YouTube playlists based on various metrics such as views, likes/comments count, and more. Initially, creating this tool was not my goal. I simply wanted to sort a playlist. Here is the method I used, which involves a single bash one-liner using standard command line tools.

Suppose we want the videos in [this playlist](https://www.youtube.com/playlist?list=PLZHQObOWTQDMsr9K-rj53DwVRMYO3t5Yr 'The essence of calculus - 3Blue1Brown') sorted by their view counts in this format:

```json
[
  {
    "title": "The essence of calculus",
    "views": 7340027
  },
  {
    "title": "The other way to visualize derivatives | Chapter 12, Essence of calculus",
    "views": 3339209
  },
  ...
]
```

We can do this using tools that are readily available on most Unix-like systems:

- `curl`: A command line HTTP client used to send requests to endpoints.
- `jq`: A JSON processor that lets you transform JSON data into desired format with a powerful syntax. We will use jq to transform the response into the output format we want and sort the output by various keys. If your system doesn't have jq installed, see [jq's installation guide](https://stedolan.github.io/jq/download/).
- `xargs`: Commands like `grep` or `awk` can take input from command line arguments as well as `stdin` (output from one command can be piped into them as input) but `curl` can only take input as arguments. We will use xargs to take each video ID from the playlist, construct an endpoint that we can send requests to, and pass it as an argument for curl.

Because of how YouTube's Data API v3 works, we cannot get all the information about every video in a playlist in a single request. First, we need to hit the [playlistItems](https://developers.google.com/youtube/v3/docs/playlistItems/list) endpoint to get the relevant video IDs, then call the [videos](https://developers.google.com/youtube/v3/docs/videos/list) endpoint for each ID for details.

Before you begin, obtain an API key from [Google Developer Console](https://console.cloud.google.com/) for YouTube Data API. Then set it as an environment variable by running the following command:

```bash
# Don't add any spaces around the = sign
$ export YT_KEY="your api key"

# Add these as well in order to shorten our command
$ export YT_API="https://www.googleapis.com/youtube/v3"
$ export YT_LISTID="PLZHQObOWTQDMsr9K-rj53DwVRMYO3t5Yr" # Playlist ID
```

By doing this, we don't have to type in these frequently needed values in our command every time we run it. We can refer to them with their keys when we need to, like this:

```bash
# -s flag makes curl silent
$ curl -s "$YT_API/playlistItems?part=contentDetails&playlistId=$YT_LISTID&maxResults=50&key=$YT_KEY"
{
  "kind": "youtube#playlistItemListResponse",
  "etag": "jhS4gzCu5OcX8oftAWInXrLHoZs",
  "items": [
    {
      "kind": "youtube#playlistItem",
      "etag": "ppImKV3lyokspBcbXkImZ7cmdEk",
      "id": "UExaSFFPYk9XVFFETXNyOUstcmo1M0R3VlJNWU8zdDVZci41NkI0NEY2RDEwNTU3Q0M2",
      "contentDetails": {
        "videoId": "WUvTyaaNkzM",
        "videoPublishedAt": "2017-04-28T15:58:48Z"
      }
    },
    {
      "kind": "youtube#playlistItem",
      "etag": "a29dMgyS-DwQYMokKGzUXy4H26U",
      "id": "UExaSFFPYk9XVFFETXNyOUstcmo1M0R3VlJNWU8zdDVZci4yODlGNEE0NkRGMEEzMEQy",
      "contentDetails": {
        "videoId": "9vKqVkMQHKk",
        "videoPublishedAt": "2017-04-29T16:24:03Z"
      }
    },
    ...
  "pageInfo": {
    "totalResults": 12,
    "resultsPerPage": 50
  }
}
```

This gets us a response with all the video IDs of interest, along with a bunch of information that we don't need. To strip this out and get just the information that we care about, we can pipe this response and use jq's filter syntax:

```bash
# Hit up arrow to replace ... with previous command
$ ... | jq '.items[].contentDetails.videoId'
"WUvTyaaNkzM"
"9vKqVkMQHKk"
"S0_qX4VJhMQ"
...
```

Now we need to loop through each line in this output and call curl requesting the /videos endpoint on each record:

```bash
# `-I {}` means replace the string `{}` in the URL with the current record (video id)
$ ... | xargs -I {} curl -s "$YT_API/videos?part=snippet,statistics&id={}&key=$YT_KEY"
{
  "kind": "youtube#videoListResponse",
  "etag": "LWiYRbWgpdH_mL3I4BdCu-yKxio",
  "items": [
    {
      ...
      "snippet": {
        "publishedAt": "2017-04-28T15:58:48Z",
        "channelId": "UCYO_jab_esuFRV4b17AJtAw",
        "title": "The essence of calculus",
        ...
       }
      "statistics": {
        "viewCount": "7363949",
        "likeCount": "207221",
        "favoriteCount": "0",
        "commentCount": "6365"
      }
      ...
    }
  ],
  ...
}
{
  "kind": "youtube#videoListResponse",
  "etag": "tY3Oi8Po8BhCGIRaZNJR5NfkVWA",
  "items": [
    {
      ...
      "statistics": {
        "viewCount": "2986293",
        "likeCount": "76291",
      }
      ...
    }
  ],
  ...
}
...
```

To strip the output of unnecessary info, we can use jq's [object construction syntax](https://stedolan.github.io/jq/manual/#TypesandValues):

Notice the `|tonumber` after viewCount. This will cast the string value into a number.

```bash
$ ... | jq '{title: .items[0].snippet.title, views: .items[0].statistics.viewCount|tonumber}'
{
  "title": "The essence of calculus",
  "views": 7363968
}
{
  "title": "The paradox of the derivative | Chapter 2, Essence of calculus",
  "views": 2986300
}
...
```

Finally, we can sort this output using the `views` key and reverse it to get the results in descending order:

```bash
# With --slurp/-s, we read the entire stream of JSON objects above into a large array
# and then run the filter on the array instead of running it on each object
$ ... | jq --slurp '.|=sort_by(.views)|reverse'
[
  {
    "title": "The essence of calculus",
    "views": 7364088
  },
  {
    "title": "The other way to visualize derivatives | Chapter 12, Essence of calculus",
    "views": 3344817
  },
  ...
]
```

## tl;dr

The final command comes out to be this:

```bash
$ export YT_KEY="your api key"
$ export YT_API="https://www.googleapis.com/youtube/v3"
$ export YT_LISTID="PLZHQObOWTQDMsr9K-rj53DwVRMYO3t5Yr" # Playlist id

$ curl -s "$YT_API/playlistItems?part=contentDetails&playlistId=$YT_LISTID&maxResults=50&key=$YT_KEY" | \
  jq '.items[].contentDetails.videoId' | \
  xargs -I {} curl -s "$YT_API/videos?part=snippet,statistics&id={}&key=$YT_KEY" | \
  jq '{title: .items[0].snippet.title, views: .items[0].statistics.viewCount|tonumber}' | \
  jq --slurp '.|=sort_by(.views)|reverse'
```

## Caveats

Currently this script does not work for playlists with 50+ videos because you can only get upto 50 results in a single request to the /playlistItems endpoint. However, plsort handles this correctly if you need that.

]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bulk convert Twitter IDs to usernames without Twitter API]]></title>
            <link>https://rspk.org/weblog/twitter-id</link>
            <guid>https://rspk.org/weblog/twitter-id</guid>
            <pubDate>Mon, 18 Apr 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Bulk convert Twitter IDs to usernames without Twitter API]]></description>
            <content:encoded><![CDATA[
I recently decided to wipe out my Twitter profile and downloaded an archive of my data. It turns out the archive only includes a big list of permanent Twitter user IDs but I needed the corresponding usernames in order to obtain RSS feeds from [nitter.net](https://nitter.net).

Getting the username is as easy as following what this URL redirects to: [twitter.com/intent/user?user_id=\<ID\>](https://twitter.com/intent/user?user_id=12). But doing this is bulk means using the Twitter API in some way which requires verifying your phone number and I didn't want to deal with credentials for this temporary task.

Meanwhile, I found [tweeterid.com](https://tweeterid.com). It allows you to convert Twitter IDs to screen names but you have to do it one by one.

Inspecting the network tab in the developer console, I found that it makes a POST request to `https://tweeterid.com/ajax.php` with the data `input=<ID>`. I made a file named `twids` with each line containing one ID, and ran the following command in my terminal. It takes each line of `twids` file, makes a POST request with the data, and writes out the response to stdout separated by newline characters.

```shell
xargs -I {} curl -s -d 'input={}' 'https://tweeterid.com/ajax.php' -w '\n' < twids
```
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introducing plsort]]></title>
            <link>https://rspk.org/weblog/pl-sort</link>
            <guid>https://rspk.org/weblog/pl-sort</guid>
            <pubDate>Thu, 17 Feb 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Introducing plsort]]></description>
            <content:encoded><![CDATA[
For some reason, YouTube doesn't let you sort playlists by criteria like views and lengths of videos.

So, I made [plsort](https://plsort.netlify.app). It allows you to enter a playlist link or id and sort it by views, duration of videos, like/dislike counts, title (alphabetically), date of publication, and of course, the default order.

It also shows you the total duration of the playlist.

For fun, I tried to make the interface as similar to YouTube's official design as I could.

If you need this frequently, you can install the [extension](https://addons.mozilla.org/en-US/firefox/addon/pl-sort) for your browser. This adds an option to open a playlist directly in plsort by adding an "**Open in plsort**" option to context menu when YouTube playlist links are right-clicked.

To contribute, submit your issues and feedback on [GitHub](https://github.com/rsapkf/plsort).

![plsort](/img/weblog/plsort.png)
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A definitive guide to 4-look last layer (CFOP)]]></title>
            <link>https://rspk.org/weblog/4lll</link>
            <guid>https://rspk.org/weblog/4lll</guid>
            <pubDate>Sun, 07 Feb 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[A definitive guide to 4-look last layer (CFOP)]]></description>
            <content:encoded><![CDATA[
In this guide, I will explain how 4-look last layer (4LLL) in CFOP works and provide the best algs and recommendations that will help to learn the full set later down the road.

Basically, the idea is to solve the last layer in no more than 4 steps. First you orient the last layer using a maximum of 2 algs and then permute the last layer using a max of 2 algs:

1. Edge Orientation (3 algs)
2. Corner Orientation (7 algs)
3. Corner Permutation (2 algs)
4. Edge Permutation (4 algs)

The total number of algs required for 4-look last layer is 10+6 = 16 whereas full OLL and PLL consist of a total of 57+21 = 78 algs. For beginners, this makes it a lot less intimidating and a lot more sense to learn 4-look LL first.

## 2-look OLL (10 algs)

**Steps**:

1. Orient the edges using one of 3 algs
2. Orient the corners using one of 7 algs

After F2L, there are 3 cases (excluding the one where all 4 edges are already oriented) that you can get:

- I shape (2 opposite edges oriented)
- L shape (2 adjacent edges oriented)
- Dot (No edges oriented)

| Cases                                                                  | Algs                                                                                             | Notes    |
| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | -------- |
| ![45](https://github.com/rsapkf/20/raw/main/3x3/cfop/oll/svg/45.svg) | <ul><li>F (R U R' U') F'</li></ul>                                                               | OLL 45   |
| ![44](https://github.com/rsapkf/20/raw/main/3x3/cfop/oll/svg/44.svg) | <ul><li>f (R U R' U') f'</li><li>y2 F (U R U' R') F'</li></ul>                                   | P/OLL 44 |
| ![2](https://github.com/rsapkf/20/raw/main/3x3/cfop/oll/svg/2.svg)   | <ul><li>F (R U R' U') F' f (R U R' U') f'</li><li>F (R U R' U') F' U2 F (U R U' R') F'</li></ul> | OLL 2    |

Instead of using specifically OLL 45/44/2 algs, you could use one of many algs for cases that have I shape (27 OLLs), L shape (15 OLLs) and a Dot (8 OLLs) in them respectively. These algs are usually recommended because they are the easist to learn.

After the edges are correctly oriented, you can get one of the following 7 cases. I highly recommend learning alternative algs for more common cases like Sune and Antisune because a lot of OLLs have variations of Sune, Antisune and their mirrors in them. This will make it easier to learn full OLL.

| Cases                                                                  | Algs                                                                                                                    | Notes                    |
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| ![27](https://github.com/rsapkf/20/raw/main/3x3/cfop/oll/svg/27.svg) | <ul><li>(R U R' U R U2 R')</li><li>y' R' U2 R U R' U R</li></ul>                                                        | Sune/OLL 27              |
| ![26](https://github.com/rsapkf/20/raw/main/3x3/cfop/oll/svg/26.svg) | <ul><li>(R U2 R' U' R U' R')</li><li>y' R' U' R U' R' U2 R</li></ul>                                                    | Antisune/OLL 26          |
| ![23](https://github.com/rsapkf/20/raw/main/3x3/cfop/oll/svg/23.svg) | <ul><li>R2 D (R' U2 R) D' (R' U2 R')</li><li>y2 R2 D' (R U2 R') D (R U2 R)</li></ul>                                    | U/OLL 23                 |
| ![24](https://github.com/rsapkf/20/raw/main/3x3/cfop/oll/svg/24.svg) | <ul><li>(r U R' U') (r' F R F')</li><li>y (R U R D) (R' U' R D') R2</li></ul>                                           | T/OLL 24                 |
| ![25](https://github.com/rsapkf/20/raw/main/3x3/cfop/oll/svg/25.svg) | <ul><li>y F' (r U R' U') r' F R</li></ul>                                                                               | L/Bowtie/OLL 25          |
| ![22](https://github.com/rsapkf/20/raw/main/3x3/cfop/oll/svg/22.svg) | <ul><li>R U2' (R2' U') (R2 U') R2' U2' R</li></ul>                                                                      | Pi/Air Jeff/Bruno/OLL 22 |
| ![21](https://github.com/rsapkf/20/raw/main/3x3/cfop/oll/svg/21.svg) | <ul><li>(R U2 R') (U' R U R') (U' R U' R')</li><li>y (R U R' U) (R U' R' U) R U2 R'</li><li>F (R U R' U')3 F'</li></ul> | H/OLL 21                 |

## 2-look PLL (6 algs)

**Steps**:

1. Permute the corners using one of 2 algs
2. Permute the edges using one of 4 algs

When you are done with OLL, there are broadly 2 cases (excluding the one where all 4 corners are already permuted) that can appear:

- Headlights (One set of adjacent corners need to be swapped)

  Put the headlights on the left and apply T Perm.

- No headlights (One set of diagonal corners need to be swapped)

  Apply Y/Nb Perm from any angle.

Remember, instead of T and Y/Nb perms, any other algorithm that swaps one set of adjacent corners (Ra, Rb, Ja, Jb, F) or diagonal corners (Na, V) while swapping one set of edges can be used respectively. Since T and Y/Nb perms are the easiest to learn, these are usually recommended.

| Cases                                                                | Algs                                                                                                                 | Notes     |
| -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | --------- |
| ![T](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/T.svg) | <ul><li>(R U R' U') (R' F R2 U') R' U' (R U R' F')</li></ul>                                                         | T Perm    |
| ![Y](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Y.svg) | <ul><li>F (R U' R' U') (R U R' F') (R U R' U') (R' F R F')</li><li>(R' U L' U2 R U' L) (R' U L' U2 R U' L)</li></ul> | Y/Nb Perm |

After permuting the corners, apply one of these 4 algs to finally solve the cube:

| Cases                                                                  | Algs                                                                                                                              | Notes   |
| ---------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------- |
| ![Ub](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Ub.svg) | <ul><li>R2 U (R U R' U') R' U' (R' U R')</li><li>y2 (R' U R' U') R' U' (R' U R U) R2</li></ul>                                    | Ub Perm |
| ![Ua](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Ua.svg) | <ul><li>(R U' R U) R U (R U' R' U') R2</li><li>L2 U' (L' U' L U) L U (L U' L)</li><li>y2 (R2 U' R' U') R U R U (R U' R)</li></ul> | Ua Perm |
| ![H](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/H.svg)   | <ul><li>(M2 U M2) U2 (M2 U M2)</li></ul>                                                                                          | H Perm  |
| ![Z](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Z.svg)   | <ul><li>(M2 U M2 U) (M' U2) (M2 U2 M')</li><li>y/y' (M2 U' M2 U') (M' U2) (M2 U2 M')</li></ul>                                    | Z Perm  |

Grok these algs and with good fingertricks, hardware, and decent lookahead during F2L, it's actually possible to be sub 30 on the 3x3.

## 3-look last layer

If you have learned 4LLL, the transition to learning the full set of algs for 2LLL is not as easy. It is recommended to learn full PLL first, then gradually pickup the remaining OLLs. This phase is known as "3LLL" or "3-look last layer". You would have to learn at least 10+21=31 algorithms to be able to solve the last layer in at most 3 algs.

## Further reading

- [CubeSkills - 4 Look Last Layer Algorithms](https://www.cubeskills.com/uploads/pdf/tutorials/4-look-last-layer.pdf)
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Use RSS for privacy and efficiency]]></title>
            <link>https://rspk.org/weblog/rss-for-privacy</link>
            <guid>https://rspk.org/weblog/rss-for-privacy</guid>
            <pubDate>Fri, 15 Jan 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Use RSS for privacy and efficiency]]></description>
            <content:encoded><![CDATA[
Social media sites are riddled with ads, trackers, and dark patterns. As much as I'd love to avoid using them for privacy reasons or time concerns, it's just not possible to keep up to date with current events without them.

[RSS](https://en.wikipedia.org/wiki/RSS) ("Really Simple Syndication") gives you the best of both worlds. For the uninitiated, it is a way to "subscribe" to websites without having to browse them individually or signing up for newsletters. With a collection of RSS feeds, you can keep up with the latest updates from multiple sources (that offer an RSS feed) from within your feed reader.

Popular sites that offer private/public RSS feeds include GitHub, YouTube, Reddit, and Stack Overflow. There are ways to get RSS feeds for other sites like Twitter and Instagram. In this document, in addition to how to use and manage your RSS subscriptions, I want to suggest an overall general privacy tip to reduce your digital footprint by visiting privacy-respecting alternative frontends for social media sites like Reddit and YouTube.

## Moving everything to RSS

This might sound somewhat _selfish_ but I have stopped subscribing/following anything/anyone online. When every interaction on a site is being tracked and used against you to sell your data and serve targeted ads, I don't think this is surprising.

Instead I use my RSS feed reader. There is no need to log into YouTube and "subscribe" to channels to view new uploads from my favorite channels or "join" subreddits to keep up with top posts on Reddit, or "follow" an account on Twitter. These are things that can be managed with a good feed reader. There are excellent open source solutions to manage your feeds for various platforms:

- [Newsboat](https://newsboat.org) for blogs and text-based sites

  Newsboat is a commandline feed reader for \*nix systems that reads a list of feeds from a plain text file. It provides a powerful syntax to manage feeds with tags/folders, custom title, feed collections, create filtered lists, etc.

- [NewPipe](https://newpipe.net) for YouTube

  NewPipe is a third-party Android app that allows you to import subscriptions directly from `subscriptions.csv` provided by [Google Takeout](https://takeout.google.com). You can also add more subscriptions, save/create playlists, create custom channel groups and save them locally without using YouTube's interface.

  Other apps that provide similar functionality are [LibreTube](https://github.com/Libre-tube/LibreTube) for Android and [FreeTube](https://freetubeapp.io/) for desktop.

- [AntennaPod](https://antennapod.org) for podcasts

  In addition to an ad-free experience, it provides the ability to sync your feeds online with a [gPodder](https://gpodder.net/) account.

All of these apps support import/export so you can always be in control of your data.

There are cloud based services to manage your list like [Newsblur](https://github.com/samuelclay/NewsBlur), [QuiteRSS](https://github.com/QuiteRSS/quiterss), [Inoreader](https://www.inoreader.com), or [Feedly](https://feedly.com) as well as self-hosted solutions like [TinyTinyRSS](https://tt-rss.org/), or [FreshRSS](https://github.com/FreshRSS/FreshRSS).

## Getting RSS feeds for various sites

Some sites like YouTube still support RSS feed for resources on their sites while others like Twitter do not. This section documents direct links to RSS feeds for various popular platforms. Use these tips to create your own collection or use a pre-made collection like [this one](https://github.com/peterc/engblogs).

### Hacker News

- Front page: [news.ycombinator.com/rss](https://news.ycombinator.com/rss)
- Show HN: [news.ycombinator.com/showrss](https://news.ycombinator.com/showrss)
- [hnrss](https:/hnrss.github.io) is an open-source project that provides RSS feeds for various HN resources. Some examples:
  - New threads with keyword "django": [hnrss.org/newest?q=django](https://hnrss.org/newest?q=django)
  - New posts submitted by user 'dang': [hnrss.org/submitted?id=dang](https://hnrss.org/submitted?id=dang)
  - New posts with 100+ upvotes and 25+ comments: [hnrss.org/show?points=100&comments=25](https://hnrss.org/show?points=100&comments=25)

### Lobsters

- Front page: [lobste.rs/rss](https://lobste.rs/rss)
- Tag(s)
  - [lobste.rs/t/programming.rss](https://lobste.rs/t/programming.rss) ([web](https://lobste.rs/t/programming))
  - [lobste.rs/t/programming,compsci.rss](https://lobste.rs/t/programming,compsci.rss) ([web](https://lobste.rs/t/programming,compsci))

### Reddit

Append `.rss` at the end of a URL:

- Front page: [https://reddit.com/.rss](https://www.reddit.com/.rss)
- Subreddit:
  - [reddit.com/r/programming.rss](https://reddit.com/r/programming.rss)
  - [reddit.com/r/LifeProTips/top.rss](https://www.reddit.com/r/LifeProTips/top.rss)
- User: [reddit.com/u/spez.rss](https://reddit.com/u/spez.rss)
- Multireddit:
  - [reddit.com/user/reddit/m/admin_faves.rss](https://www.reddit.com/user/reddit/m/admin_faves.rss)
  - [reddit.com/r/programming+javascript+unixporn.rss](https://reddit.com/r/programming+javascript+unixporn.rss)

### YouTube

- Channel: [youtube.com/feeds/videos.xml?channel_id=UCYO_jab_esuFRV4b17AJtAw](https://www.youtube.com/feeds/videos.xml?channel_id=UCYO_jab_esuFRV4b17AJtAw)
- Playlist: [youtube.com/feeds/videos.xml?playlist_id=PL3A5849BDE0581B19](https://www.youtube.com/feeds/videos.xml?playlist_id=PL3A5849BDE0581B19)

### GitHub

- Releases: [github.com/3b1b/manim/releases.atom](https://github.com/3b1b/manim/releases.atom)
- Commits: [github.com/3b1b/manim/commits/master.atom](https://github.com/3b1b/manim/commits/master.atom)
- Tags: [github.com/3b1b/manim/tags.atom](https://github.com/3b1b/manim/tags.atom)

For a private feed for your recent activity, you can click on "Subscribe to your news feed" at the bottom of your GitHub homepage.

### GitLab

- Activity: [gitlab.com/inkscape/inkscape.atom](https://gitlab.com/inkscape/inkscape.atom)
- Commits: [gitlab.com/inkscape/inkscape/-/commits/master?format=atom](https://gitlab.com/inkscape/inkscape/-/commits/master?format=atom)
- Tags: [gitlab.com/inkscape/inkscape/-/tags/master?format=atom](https://gitlab.com/inkscape/inkscape/-/tags?format=atom)
- Issues: [gitlab.com/inkscape/inkscape/-/issues.atom?state=opened](https://gitlab.com/inkscape/inkscape/-/issues.atom?state=opened)

### Stack Overflow

- Tag(s)
  - [stackoverflow.com/feeds/tag?tagnames=haskell&sort=newest](https://stackoverflow.com/feeds/tag?tagnames=haskell&sort=newest)
  - [stackoverflow.com/feeds/tag?tagnames=haskell+java&sort=newest](https://stackoverflow.com/feeds/tag?tagnames=haskell+java&sort=newest)

### Twitter

Twitter stopped providing official feeds in 2013. However, you can use [nitter.net](https://nitter.net) for this.

- User: [nitter.net/xkcd/rss](https://nitter.net/xkcd/rss)

### Mastodon

- User: [mastodon.social/@Gargron.rss](https://mastodon.social/@Gargron.rss) or [mastodon.social/users/Gargron.atom](https://mastodon.social/users/Gargron.atom)

### Medium

- User: [medium.com/feed/@Medium](https://medium.com/feed/@Medium)
- Tag: [medium.com/feed/tag/programming](https://medium.com/feed/tag/programming)<br />
  [More info](https://help.medium.com/hc/en-us/articles/214874118-RSS-feeds)

### Instagram

Bibliogram, a third party alternative instagram front-end provides RSS/Atom feeds for Instagram accounts:

- User: [bibliogram.art/u/starwars/rss.xml](https://bibliogram.art/u/starwars/rss.xml) or [bibliogram.art/u/starwars/atom.xml](https://bibliogram.art/u/starwars/atom.xml)

For sites that don't offer accessible RSS/Atom feeds like Facebook or Twitch, the [RSS-Bridge](https://rss-bridge.github.io/rss-bridge/) project provides feeds based on “bridges” developed for these sites. Consider using it if necessary.

[This gist](https://gist.github.com/thefranke/63853a6f8c499dc97bc17838f6cedcc2) has RSS links to many other platforms.

## Removing apps from your phone

In addition to taking up more space and bandwidth, mobile apps have more direct access to your personal information than websites. However, a browser configured with an adblocker like uBlock Origin and Privacy Badger along with built-in privacy options (geolocation/fingerprinting blocker, Multi-Account Containers, and Enhanced Tracking Protection on Firefox, etc) enabled will work across all sites you visit and block all sorts of trackers embedded in them.

If you are willing to give up a little bit of convenience for privacy, consider using these privacy-respecting frontends:

- [nitter](https://nitter.net/) for Twitter
- [teddit](https://teddit.net/) / [libreddit](https://libredd.it/) for Reddit
- [Bibliogram](https://bibliogram.art/) for Instagram

But the question remains, what about features like multireddits or Twitter lists? Well there are ways around it.

For Reddit, if your custom feed has &lt;25 subreddits, you can use the `+` trick to join multiple subreddit feeds and save the link as a bookmark. For example: [reddit.com/r/pics+videos+wtf](https://www.reddit.com/r/pics+videos+wtf) and [reddit.com/r/pics+videos+wtf.rss](https://reddit.com/r/pics+videos+wtf.rss).

There are public multireddits/Twitter lists created by other users for different topics like programming, Linux, security, cats, etc. Just search one and use it.

Use mobile sites if necessary. Most websites have perfectly usable mobile-responsive websites. If you must use mobile apps, you can use [NetGuard](https://github.com/M66B/NetGuard) to block access to the internet on a per-app basis.

## Conclusion

RSS is a really neat way of managing subscriptions while saving a lot of time. I urge all bloggers and webmasters to add an RSS/Atom feed to their site and respect their users instead of displaying an ugly, distracting modal asking for an email.

There are official and third-party ways to get RSS feeds for various websites. There are good open source feed readers to manage these subscriptions. Use alternative sites/apps instead of mobile apps to reduce data leaks.

Use RSS for privacy and efficiency.
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[PLL recognition guide]]></title>
            <link>https://rspk.org/weblog/pll-recognition</link>
            <guid>https://rspk.org/weblog/pll-recognition</guid>
            <pubDate>Mon, 04 Jan 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[PLL recognition guide]]></description>
            <content:encoded><![CDATA[
One overlooked aspect of teaching PLL to beginner CFOP solvers in many tutorials online is PLL recognition. Unlike OLL cases that are generally easier to identify, have shorter algorithms and familiar triggers, PLL algorithms are longer and recognizing PLL cases takes a bit of effort. If you are a beginner CFOP learner who knows the basics (2-look PLL) and want to learn full PLL and find the task daunting, this guide will help you recognize, learn and practice PLL algs in no time.

PLL cases are usually represented by drawing arrows showing which pieces need to swap places. They are named using English alphabets which are chosen to roughly resemble the pattern of these arrows. But that can sometimes be confusing. So to give a simpler and more efficient way, I will completely omit the arrows from visualizations and will go over some basics first.

Out of 21 cases, only 13 are distinct. Other PLLs are either mirrors or inverses. For e.g. Ua perm is the mirror of Ub, meaning if you apply Ua alg to Ub case but by mirroring your moves with the left hand, you'll be able to solve Ub.

The 13 distinct perms are:

- U (Ua, Ub) \*
- H \*
- Z \*
- T (Adjacent corner swap alg in 2-look PLL) \*
- Y (Diagonal corner swap alg in 2-look PLL) \*
- F
- E
- V
- A (Aa, Ab)
- J (Ja, Jb)
- R (Ra, Rb)
- N (Na, Nb)
- G (Ga, Gb, Gc, Gd)

_\* 2-look PLL algs_

To recognize the rest, we will look whether certain "features" are available. There are 5 main features that are easily noticeable in PLL cases:

## Headlights

If you see one or more face on the last layer (LL) where two corner stickers have the same color and the middle one has either an adjacent or an opposite color, that's a headlight. 11 out of 21 PLLs have a headlights feature.

A "connected headlight" is when a 1x2 block is connected to a headlight. Depending on the case, there can be either 0, 1 or 2 connected headlights.

PLLs: **A**, **T**, **R**, **G** (+ **H**, **Z**)

| Name               | PLL                                                                                                                                                                                                                                                                                         | Notes                                                                                                                                  |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| A (Aa, Ab)         | ![Aa](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Aa.svg) ![Ab](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Ab.svg)                                                                                                                                               | <ul><li>Adjacent color between headlights</li><li>No connected headlights</li></ul>                                                    |
| R (Ra, Rb)         | ![Ra](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Ra.svg) ![Rb](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Rb.svg)                                                                                                                                               | <ul><li>Adjacent color between headlights</li><li>1 connected headlight</li></ul>                                                      |
| T                  | ![T](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/T.svg)                                                                                                                                                                                                                        | <ul><li>Opposite color between headlights</li><li>2 connected headlights</li></ul>                                                     |
| G (Ga, Gb, Gc, Gd) | ![Ga](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Ga.svg) ![Gb](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Gb.svg) ![Gc](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Gc.svg) ![Gd](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Gd.svg) | <ul><li>Ga/Gc - Adjacent color between headlights, Gb/Gd - Opposite color between headlights</li><li>No connected headlights</li></ul> |
| H                  | ![H](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/H.svg)                                                                                                                                                                                                                        | <ul><li>Headlights on all sides</li><li>No connected headlights</li></ul>                                                              |
| Z                  | ![Z](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Z.svg)                                                                                                                                                                                                                        | <ul><li>Headlights on all sides</li><li>No connected headlights</li></ul>                                                              |

## 1x3 block

Only 5 out of 21 cases have a 1x3 block of solved pieces on the last layer.

PLLs: **J**, **F**, **U**

| Name       | PLL                                                                                                                                           | Notes                                                                                       |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| J (Ja, Jb) | ![Ja](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Ja.svg) ![Jb](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Jb.svg) | <ul><li>1 connected 1x3 block</li><li>Adjacent colors pair next to the 1x3 block</li></ul>  |
| F          | ![F](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/F.svg)                                                                          | <ul><li>No connected 1x3 block</li><li>Opposite colors pair next to the 1x3 block</li></ul> |
| U (Ua, Ub) | ![Ua](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Ua.svg) ![Ub](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Ub.svg) | <ul><li>Easy cases</li></ul>                                                                |

## 1x2 block

10 PLLs have 1x2 blocks on the last layer in them.

PLLs: **J**, **N**, **Y**, **G**

| Name               | PLL                                                                                                                                                                                                                                                                                         | Notes                                        |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| J (Ja, Jb)         | ![Ja](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Ja.svg) ![Jb](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Jb.svg)                                                                                                                                               | <ul><li>3 1x2 blocks (+1 full bar)</li></ul> |
| N (Na, Nb)         | ![Na](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Na.svg) ![Nb](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Nb.svg)                                                                                                                                               | <ul><li>4 distinct 1x2 blocks</li></ul>      |
| Y                  | ![Y](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Y.svg)                                                                                                                                                                                                                        | <ul><li>2 1x2 blocks</li></ul>               |
| T                  | ![T](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/T.svg)                                                                                                                                                                                                                        | <ul><li>2 1x2 blocks</li></ul>               |
| G (Ga, Gb, Gc, Gd) | ![Ga](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Ga.svg) ![Gb](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Gb.svg) ![Gc](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Gc.svg) ![Gd](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Gd.svg) | <ul><li>1 1x2 block</li></ul>                |

## 2x2 square

This is just a solved 2x2 block on the top layer. 3 PLL cases have this feature.

PLLs: **A**, **V**

| Name       | PLL                                                                                                                                           | Notes                                                                            |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| A (Aa, Ab) | ![Aa](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Aa.svg) ![Ab](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/Ab.svg) | <ul><li>Has headlights</li><li>Same color in the diagonal corners</li></ul>      |
| V          | ![V](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/V.svg)                                                                          | <ul><li>No headlights</li><li>Different colors in the diagonal corners</li></ul> |

## No features

PLLs: **E**

![E](https://github.com/rsapkf/20/raw/main/3x3/cfop/pll/svg/E.svg)

The main feature of the **E** perm is that it has no features. You can look around the last layer and see that there aren't any headlights, full bars, half-bars, or 2x2 squares.

This is just a beginner's guide to PLL recognition. There are advanced techniques that can help you recognize PLL by looking at just 2 sides of the last layer. Check out the links below to learn more.

## Further reading

- [badmephisto - Recognizing the PLLs (2009)](https://www.youtube.com/watch?v=qBYycb7hR4Y/)
- [CubeSkills - PLL Algs](https://www.cubeskills.com/uploads/pdf/tutorials/pll-algorithms.pdf)
- [CubeSkills - PLL Recognition Trainer](https://www.cubeskills.com/tools/pll-recognition-trainer) - 2 side PLL recognition trainer.
- [AlgDb.net - PLL](http://algdb.net/puzzle/333/pll/)
- [JPerm.net - PLL Trainer](http://jperm.net/algs/pll)
- [Speedsolving Wiki - PLL](https://www.speedsolving.com/wiki/index.php/PLL/)
- [How I Recognize PLL Fast](https://www.youtube.com/watch?v=sdz5EjWShCg), [Advanced PLL Recognition](https://www.youtube.com/watch?v=_gIAQm_B170) - J Perm

## Credits

All visualizations in this guide were created using [VisualCube](http://cube.rider.biz/visualcube.php).
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Movie review: Tesla (2020)]]></title>
            <link>https://rspk.org/weblog/tesla-2020</link>
            <guid>https://rspk.org/weblog/tesla-2020</guid>
            <pubDate>Thu, 17 Sep 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Movie review: Tesla (2020)]]></description>
            <content:encoded><![CDATA[
I watched this movie expecting a thoughtful biographical drama about one of the most interesting figures in the history of science. Unfortunately, it turned out to be dull, and uninformative, despite its occasional stylistic ambition.

The movie feels more like an avant-garde experiment than a work with real substance. It jumps between timelines, often feels distracting, as if the film is unsure whether it wants to be a serious biography or a commentary on how to tell historical stories. The pacing doesn’t help either. Large portions of the movie feel boring, with long scenes that don’t reveal anything new. By the end, the film had covered a fair amount but said very little.

Much of the movie shows familiar themes: Tesla-Edison rivalry, his eccentric nature, and financial struggles without adding much insight. Technical ideas are mentioned but Tesla’s actual work remains frustratingly abstract. And then, there's the karaoke ending.

If you’re interested in Nikola Tesla himself, you’re better off skipping the movie entirely and reading Tesla’s own [autobiographical writings](https://en.wikipedia.org/wiki/My_Inventions%3A_The_Autobiography_of_Nikola_Tesla). In his own words, Tesla comes across as more vivid, and insightful than anything the film manages to convey. For anyone genuinely curious about Tesla’s life or ideas, reading his work is both more engaging and far more rewarding.
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A beginner's guide to digital privacy]]></title>
            <link>https://rspk.org/weblog/digital-privacy</link>
            <guid>https://rspk.org/weblog/digital-privacy</guid>
            <pubDate>Sat, 12 Sep 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[A beginner's guide to digital privacy]]></description>
            <content:encoded><![CDATA[
_\* Slightly advanced tips_

## Basics

- Understand why maintaining privacy is important. Start here:

  - [Privacy Guides](https://www.privacyguides.org/en/basics/why-privacy-matters/)
  - [r/privacy wiki](https://old.reddit.com/r/privacy/wiki)
  - [Permanent Record - Edward Snowden](<https://en.wikipedia.org/wiki/Permanent_Record_(autobiography)>)

- Always look for well known open source alternatives to services that you use:

  - [AlternativeTo.net](https://alternativeto.net/)
  - [ethical.net](https://ethical.net/)
  - [Restore Privacy](https://restoreprivacy.com/)

- Start by using reliable open source web browsers:

  - [Firefox](https://www.mozilla.org/en-US/firefox/)
  - [Ungoogled Chromium](https://github.com/Eloston/ungoogled-chromium)
  - [Tor](https://torproject.org)

- Install privacy addons:

  - [Privacy Badger](https://github.com/EFForg/privacybadger/)
  - [uBlock Origin](https://github.com/gorhill/uBlock/)
  - [Decentraleyes](https://decentraleyes.org/)
  - [Firefox Multi-Account Containers](https://addons.mozilla.org/en-US/firefox/addon/multi-account-containers/)
  - [ClearURLs](https://gitlab.com/KevinRoebert/ClearUrls), etc.

  See also: [Recommended privacy addons](https://addons.mozilla.org/en-US/firefox/search/?category=privacy-security&sort=recommended%2Cusers&type=extension) for Firefox.

- Use a search engine that doesn't log your search queries:

  - [DuckDuckGo](https://duckduckgo.com/)
  - [Startpage](https://www.startpage.com/)
  - [Searx](https://searx.me/)
  - [Qwant](https://qwant.com/)

- Disable tracking, fingerprinting, cryptominers and telemetry from browser preferences. Clear cookies frequently, disable location access to sites and cover your webcam unless necessary.

- Quit using Google, Microsoft, Apple, Facebook products for **personal/sensitive use cases**.

  These companies are great for security of your data (for the most part), they hire top engineers from around the world but are worse for your privacy, esp. if they rely on advertisement for their revenue. If you have to use Google, go to your account settings and turn off all the [activity controls](https://myactivity.google.com/activitycontrols) and [ads personalization](https://adssettings.google.com/). Go through similar settings on your dashboard for other accounts.

- **Always** choose "Sign up with Email" while signing up on websites. ([?](https://en.wikipedia.org/wiki/OAuth))

  Unless mandatory, you should not continue with social signup (Sign up with Google/Microsoft/Twitter/Facebook/Apple/Amazon, etc).

  If you are required to do so, take care of what scopes and information you are permitting the service to use. Platforms usually provide a way to manage this info from your account settings:

  - [Twitter Connected Apps](https://twitter.com/settings/connected_apps)
  - [Facebook Apps and Websites](https://www.facebook.com/settings?tab=applications)
  - [Apps with access to your Google account](https://myaccount.google.com/permissions)
  - [Apps and services with access to your Microsoft account](https://go.microsoft.com/fwlink/p/?LinkId=829203)
  - [Manage Login with Amazon](https://www.amazon.com/ap/adam)

- Create separate email accounts/aliases for signups, newsletters, communication, banking, music, gaming, etc.

  Many email providers like ProtonMail and Google allow for unlimited aliases by adding a dot(.) in your email username. For example, `ricksanchez@protonmail.com` and `rick.sanchez@protonmail.com` or `ric.ksanch.ez@protonmail.com` are essentially the same addresses. The messages sent to either of these addresses will arrive in the same inbox. **This tip won't work for Tutanota**. These services also allow [plus addressing](https://blog.mailfence.com/plus-addressing-to-track-spammers/). For example, `john.doe@gmail.com` or `john.doe+twitter@gmail.com` are the same. You can use these tricks to create aliases for signing up on different sites without putting your main address everywhere.

- Avoid using the same username across multiple platforms and websites.

  Your first priority should be using a long, unique and random password for every site but having different usernames is also [important](https://nordpass.com/blog/unique-username-importance/). Tools like [sherlock](https://github.com/sherlock-project/sherlock) can be used to hunt down your identities across sites within seconds. Use a random [username](https://www.lastpass.com/username-generator/) [generator](https://nordpass.com/username-generator). There are [tons](https://duckduckgo.com/?q=password+40+strong&ia=answer) [of](https://bitwarden.com/password-generator/) [strong](https://www.lastpass.com/password-generator) [password/pass-phrase](https://1password.com/password-generator/) [generators](https://www.dashlane.com/features/password-generator) that you can use. Ideally, you should also use randomly generated strings as answers to your security questions for sites that require them and save them on your password manager using custom fields.

- Research in advance if the service you're signing up for allows for an (easy) way to delete your account. You might regret later when you find out you can't delete your account/subscription. Some websites have tricky account deletion procedure:

  - Amazon, Adobe (require contacting support)
  - TED (no delete account option in account settings)
  - Shopify (you must have a premium subsciption to close your store/account)

  Read this post on Hacker News: [Before buying a NYT subscription, here's what it'll take to cancel it](https://news.ycombinator.com/item?id=26174269)

- Make use of [email aliases](https://protonmail.com/support/knowledge-base/addresses-and-aliases/) or email forwarding services:

  - [AnonAddy](https://github.com/anonaddy/anonaddy)
  - [SimpleLogin](https://github.com/simple-login/app)

- Use disposable email addresses for temporary signups:

  - [Temp Mail](https://temp-mail.org/)
  - [10 Minute Mail](https://10minutemail.com/)

- Use alternative frontends for platforms like YouTube, Twitter, Reddit, and Instagram:

  - [Invidious](https://invidio.us/) (See also: [NewPipe](https://newpipe.net/) for Android.)
  - [nitter](https://nitter.net/)
  - [teddit](https://teddit.net/) / [libreddit](https://libredd.it/)
  - [Bibliogram](https://bibliogram.art/)

  Use [Privacy Redirect](https://addons.mozilla.org/en-US/firefox/addon/privacy-redirect/) addon that redirects Twitter, YouTube, Instagram & Google Maps requests to privacy friendly alternatives.

- Instead of liking/saving anything on Facebook, YouTube and social platforms, use an end-to-end encrypted bookmark service like [Firefox Sync](https://www.mozilla.com/en-US/firefox/sync/).

  Platforms these days track everything from what your see, what you click on, share or comment on, and even [how long you looked at](https://www.theverge.com/2015/6/13/8775587/facebook-news-feed-uses-time-read-to-promote-posts) a particular item on your feed. Reduce your digital footprint as much as you can. Export your personal data from Facebook, Google from your account settings and work on deleting all of it. See the "Exporting data" section below.

- Use decentralized services if you need:

  - [Mastodon](https://mastodon.social/)
  - [PixelFed](https://pixelfed.org/)
  - [Diaspora](https://diasporafoundation.org/)
  - [Fediverse](https://fediverse.party/)

- Use end-to-end encypted messaging applications for communication:

  - [Signal](https://signal.org/)
  - [Wire](https://wire.com)

- Use E2E encrypted email services:

  - [Tutanota](https://tutanota.com/)
  - [Protonmail](https://protonmail.com/)

- Use E2E encrypted note-taking apps:

  - [Standard Notes](https://standardnotes.org/)
  - [Joplin](https://joplinapp.org/) (E2EE is [not](https://joplinapp.org/e2ee/) enabled by default)

- Use E2E encrypted cloud storage solutions:

  - [Tresorit](https://tresorit.com/)
  - [pCloud Crypto](https://pcloud.com/)
  - [Sync.com](https://sync.com/)

- Encrypt your files before uploading them to Dropbox, Google Drive or Microsoft OneDrive:

  - [VeraCrypt](https://veracrypt.fr/)
  - [Cryptomator](https://cryptomator.org/)

- Use a reliable VPN:

  - [ProtonVPN](https://protonvpn.com/)
  - [WireGuard](https://www.wireguard.com/)

- \*Use a better DNS resolver:

  - [Cloudflare WARP](https://1.1.1.1/)

- \*Use Linux/BSD:

  - Made for privacy: [Tails](https://tails.boum.org/), [Whonix](https://www.whonix.org/), [Trisquel](https://trisquel.info/)...
  - Debian based: [Debian](https://www.debian.org/), [Ubuntu](https://www.ubuntu.com/)...
  - Arch based: [Arch](https://www.archlinux.org/), [ArcoLinux](https://arcolinux.info/), [Manjaro](https://manjaro.org/)...
  - BSDs: [OpenBSD](https://github.com/openbsd/src), [NetBSD](https://www.netbsd.org/), [FreeBSD](https://github.com/freebsd/freebsd/)...

- \*Switch the OS on your smartphone:

  - [LineageOS](https://lineageos.org/)
  - [Ubuntu Touch](https://ubuntu-touch.io/)
  - [GrapheneOS](https://grapheneos.org/)
  - [postmarketOS](https://postmarketos.org/)

- \*Self-host software on your own server by renting a VPS.

- \*Use [PGP](https://www.openpgp.org/) for encrypted communication.

## Security tips

- Use an open source password manager:

  - [Bitwarden](https://bitwarden.com/)
  - [Keepass](https://keepass.info/)

- Enable [multi-factor authentication](https://en.wikipedia.org/wiki/Multi-factor_authentication) on as many accounts as you can. Use an authenticator app like [FreeOTP](https://github.com/freeotp/) or [Aegis](https://github.com/beemdevelopment/Aegis/) instead of giving out your phone number to services (**Don't forget to set necessary recovery options and backup 2FA recovery codes for important accounts!**).

- Monitor if you’ve been part of an online data breach:
  - [Firefox Monitor](https://monitor.firefox.com/)
  - [Have I Been Pwned](https://haveibeenpwned.com/)

## Exporting data from platforms

Here are the direct links to delete/export personal data from some popular platforms:

### Google

- [Google Takeout](https://takeout.google.com/)
- [YouTube subscription manager](https://youtube.com/subscription_manager): Export YouTube subscriptions to OPML. `deprecated`

### Microsoft

[Microsoft account privacy dashboard](https://account.microsoft.com/privacy)

### Meta

- Facebook: [Settings > Your Facebook Information > Download Your Information](https://www.facebook.com/dyi/)
- Instagram: [Settings > Privacy and Security > Data Download](https://www.instagram.com/download/request/)
- WhatsApp: [Settings > Account > Request account info](https://faq.whatsapp.com/general/account-and-profile/how-to-request-your-account-information/)

### Apple

[Archive or make copies of the information you store in iCloud](https://support.apple.com/en-us/HT204055)

### Reddit

- To export subscriptions, go to [/subreddits](https://reddit.com/subreddits) > Right click on "**multireddit of your subscriptions**", copy and save the link.
- To delete posting/commenting history, use [j0be/PowerDeleteSuite](https://github.com/j0be/PowerDeleteSuite) or [sr33/ares](https://github.com/sr33/ares).

### Spotify

- **Account overview** > [Privacy settings](https://www.spotify.com/account/privacy/) > **Download your data**
- [Exportify](https://watsonbox.github.io/exportify/)

### Twitter

[Download an archive of your data](https://twitter.com/settings/download_your_data)

### GitHub

[Account settings](https://github.com/settings/admin) > **Export account data** > **Start export**

### Pocket

- [Export reading list](https://getpocket.com/export)
- [Delete reading list](https://getpocket.com/privacy_clear/)

### IMDb

[Your ratings](https://imdb.com/list/ratings) > **Overflow menu** > **Export**

### Goodreads

[My Books](https://www.goodreads.com/review/list) > [Import/Export](https://www.goodreads.com/review/import)

### Netflix

[Your Account](https://www.netflix.com/YourAccount) > **Profile & Parental Controls** > **Viewing activity** > **Download all**

### Discord

**User Settings** > **Privacy and Safety** > **Request all of my Data**

## Further reading

This is just a list of the most basic options for getting started on digital privacy. There are comprehensive guides, articles, books and websites for more advanced tips. Make sure to research every option thoroughly to determine what works best for you.

- [Digital Privacy](https://en.wikipedia.org/wiki/Digital_privacy)
- [Awesome-Selfhosted](https://github.com/awesome-selfhosted/awesome-selfhosted) - Free Software network services and web applications which can be hosted locally.
- [Two Factor Auth](https://twofactorauth.org/) - List of websites and whether or not they support 2FA.
- [Mozilla Blog](https://blog.mozilla.org/)
- [Electronic Frontier Foundation](https://eff.org/)
- [Prism ⚡ Break](https://prism-break.org/) - Opt out of global data surveillance programs like PRISM, XKeyscore and Tempora.
- [Privacy Guides](https://www.privacyguides.org)
- [r/privacy](https://old.reddit.com/r/privacy/)
- [The Social Dilemma - Netflix documentary](https://en.wikipedia.org/wiki/The_Social_Dilemma)
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[An overview of speedcubing (+Awesome learning resources)]]></title>
            <link>https://rspk.org/weblog/cubing-basics</link>
            <guid>https://rspk.org/weblog/cubing-basics</guid>
            <pubDate>Sun, 02 Aug 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[An overview of speedcubing (+Awesome learning resources)]]></description>
            <content:encoded><![CDATA[
When I first learned to solve the Rubik's Cube back in 2014, it took me less than 2 hours to learn the beginner's solution. But I quickly got bored. I was pulled into the deep rabbithole of speedcubing&mdash;an exercise where people try to solve the cube in the fastest time possible[^1].

This guide will give you a thorough introduction to what speedcubing is and a collection of the best resources online that you can use to achieve a sub 30 result in the fastest time possible. Even if you are already familiar with speedsolving techniques, read on, as you might discover something you didn't already know.

The method I use is called [CFOP](https://en.wikipedia.org/wiki/CFOP_method), popularized by Czech mathematician [Jessica Fridrich](http://www.ws.binghamton.edu/fridrich/system.html) in 1997. This is the most popular speedsolving method used by cubers today, among others like [ZZ](https://www.speedsolving.com/wiki/index.php/ZZ_method), [Roux](https://www.speedsolving.com/wiki/index.php/Roux_method) and [Petrus](https://www.speedsolving.com/wiki/index.php/Petrus_Method).

## Introduction to CFOP

There are 4 steps in this method:

- Cross
- F2L (First 2 Layers)
- OLL (Orientation of Last Layer)
- PLL (Permutation of Last Layer)

### Cross

![Cross](/img/weblog/cfop-cross.png)

The first step involves making a cross on one side by solving all edges of a given face and aligning the edges with the second-layer centers. Most speedcubers usually solve the cross on the bottom to avoid cube rotations and to get an overall better view of the important pieces needed for the next step. This is called '**Lookahead**'. In speedcubing competitions, cubers take their 15 second inspection time to plan required moves for a cross (in many cases, the first F2L pair as well!) to save time. In all cases, the cross can be done in less than 8 moves. Most speedcubers are color-neutral, meaning they can start with any colored face without slowing down.

### F2L

![F2L](/img/weblog/cfop-f2l.png)

In F2L, corner and edge pieces are paired up using the free top layer and later moved to their correct location. The four **slots** between the cross pieces are filled in, one slot at a time, by inserting a corner and its corresponding edge simultaneously. Most of the 42 distinct cases have reasonable intuitive solutions. So, algorithms are not required for this step unless you aim to achieve sub 25 or better results. The completion of this step leaves one with just the last layer, typically placed on top.

### OLL

![OLL](/img/weblog/cfop-oll.png)

This stage involves manipulating the top layer so that all the pieces therein have the same color on top, at the expense of incorrect colors on other sides. This stage involves a total of 57 distinct cases. Multiple algorithms (in some cases, 10+) can be used to solve a single OLL case. You just need to remember the easiest one.

A simpler version, called "**2-look**" OLL orients edges first and corners afterwards. It uses 10 algorithms, 3 for edge orientation and 7 for corner orientation.

### PLL

![PLL](/img/weblog/cfop-pll.png)

The final stage involves moving the pieces of the top layer while preserving their orientation. There is a total of 21 cases for this stage.

Just like 2-look OLL, "**2-look**" PLL solves the corners first and edges separately. It uses 6 algorithms, 2 for corner permutation and 4 for edge permutation.

### Recommended order to learn CFOP

1. Be comfortable with solving the Cross and F2L intuitively.
2. Learn 4 look last layer (2 look OLL + 2 look PLL).
3. Learn full set of PLL algorithms.
4. Learn the remaining OLL algorithms.
5. Learn advanced algorithm sets and techniques while working on your Cross and F2L efficiency (see below).

## Terminologies

There are some very common moves and terms in speedcubing that you should know.

- [Notation](https://www.speedsolving.com/wiki/index.php/NxNxN_Notation)

- Triggers

  These are 3 or 4 move sequences that commonly appear in multiple algorithms which makes them easier to memorise.

  - Sexy: `R U R' U'`
  - Sledgehammer: `R' F R F'`
  - HedgeSlammer/Reverse Sledgehammer: `F R' F' R`
  - Sune/Anti-chair: `R U R' U R U2 R'` and [variants](https://www.speedsolving.com/wiki/index.php/Sune#Variants).
  - Antisune/Chair: `R U2 R' U' R U' R'`

  For example, in [T OLLs](https://www.speedsolving.com/wiki/index.php/OLL#.22T.22_shapes), instead of memorising the full alg like `F (R U R' U') F'`, you could remember it instead like `F Sexy F'` or in case of `(R U R' U') (R' F R F')`, as `Sexy Sledgehammer`.

- [Acronyms](https://www.speedsolving.com/wiki/index.php/Category:Acronyms)
  - TPS - Turns Per Second
  - AUF - Adjust U Face

## Personal recommendations

- Learn to use the inspection time in practice solves.
- Learn fingertricks as early as possible.
  - [Finger Tricks - JPerm](https://www.youtube.com/watch?v=wLuVF9Dk3AQ)
  - [OLL Algs and Fingertricks - Feliks Zemdegs](https://www.youtube.com/watch?v=IasVqtCHoj0)
  - [PLL Algs and Fingertricks - Feliks Zemdegs](https://www.youtube.com/watch?v=HWIQdX8vHcE)
- Attempt to complete the cross in less than 8 moves. Plan out the entire cross during inspection.

  - [Advanced Cross Playlist - JPerm](https://www.youtube.com/playlist?list=PLI24ciRbl8BWbmb42GW5BDinKfKVq79WD).

  Look into [X-Cross](https://www.youtube.com/watch?v=P9POsBAaKd0) (Extended Cross). This means solving the cross plus 1 F2L pair simultaneously.

- Learn F2L intuitively. With enough practice, your muscle memory will make learning the algorithms obsolete.
  - [Advanced F2L Playlist - JPerm](https://www.youtube.com/playlist?list=PLI24ciRbl8BUTnnzmJIIn3Ts_6-mKMBP7).
- Learn 2-look OLL first **along** with other easier OLL cases.
  - Print out [this](https://www.cubeskills.com/tutorials/oll-algorithms) set of OLL algs from [Feliks Zemdegs' CubeSkills site](https://cubeskills.com/).
  - [2-look OLL - JPerm](https://www.youtube.com/watch?v=GhmYBgLoQQg).
- Learn 2-look PLL first. It is recommended to learn the entire PLL as soon as possible since there are only 21 algs on the set.

  - Print out [this](https://www.cubeskills.com/tutorials/pll-algorithms) set of PLL algs from CubeSkills.
  - [2-look PLL - JPerm](https://www.youtube.com/watch?v=f_Yor-ydZjs).

- Practice color neutrality right away. In beginning stages, OLL and PLL may seem to be overwhelming but as you learn the algorithms, that will get easier. But once you settle on a color, for e.g. white on bottom, it will be significantly harder to develop color neutrality afterward because your brain will be wired to filter out the white sticker during solves. Consequently, improving your Cross and F2L will be much harder. Color neutrality cannot be developed through algorithms like the last layer.

## Becoming sub 20

Intuitive F2L, 2-look OLL and 2-look PLL can get you to sub 30 but full OLL and full PLL can definitely help you achieve better results. You will need to practice your cross and F2L so that your muscle memory can solve F2L cases without thinking about the current pair and look ahead to the next pair while inserting the current one. Advanced F2L techniques may be required.

## Becoming sub 10 or better

The key here is to reduce cube rotations as much as you can, increasing your TPS, reducing regrips, advanced fingertricks, advanced lookahead and developing color neutrality. Learning new sets of algorithms (**COLL/OLLCP**, **Winter Variation**, **BLE**, etc) to be able to recognize cases where OLL/PLL can be skipped or that lead to an easier PLL will also help. You should probably also learn multiple algorithms for the same case in order to be able to solve a case from other angles saving you a turn, the AUF or a cube rotation. Many speedcubers also use advanced techniques like [keyhole](https://www.youtube.com/watch?v=mXEOPX42FJg), [pseudoslotting](https://www.youtube.com/watch?v=TWffMVBqj1w), [multislotting](https://www.youtube.com/watch?v=IjDYoNxDa6U) and [double/triple X-Cross](https://www.youtube.com/watch?v=pTUy0oi4Nco).

## Advanced algorithms

Getting even better times requires learning a few new sets of algorithms. These sets are often used in other speedsolving methods and can be used in combination with CFOP to get faster times. You don't need to learn every algorithm in these sets to be able to use them since there are thousands - just the more common ones. Here are some of the most popular algorithms sets:

### COLL (Corners of Last Layer)

If the last edges after F2L are already oriented, you can use a different set of 42 algorithms called COLL that solve all corners of the top layer to get either **U** (8/12 chance), **H** (2/12 chance) or **Z** (1/12 chance) perms which are relatively easier to do or a PLL skip. COLL increases your chances of getting a PLL skip from 1/72 to 1/12 which greatly helps reduce the overall time during your solves.

### OLLCP (Orientation of Last Layer and Corner Permutation)

OLLCP is the superset of COLL with 300+ algorithms than can solve the corners with a single algorithm even if not all the last edges are solved after F2L.

### OLS (Orientation of the Last Slot)

OLS is a last slot method used to skip any OLL case while simultaneously solving the last F2L pair. OLS is mainly split into two groups: **VLS (Valk Last Slot)** and **HLS (Hessler Last Slot)**. These two groups are split into 8 subsets, each, that are based on edge misorientation. The most significant subsets under these groups are **WV (Winter Variation)** and **SV (Summer Variation)** respectively. Each of these subsets has 54 algorithms, including mirrors. It is usually recommended to first learn WV, then SV, followed by the rest of VLS, and finally the rest of HLS.

This technique has a very high algorithm count, however - there are a total of at least 864 algorithms, including mirrors. That means, if one learned 2 algs per day, it would take them more than a year to learn all of them!

### ZBLS (Zborowski-Bruchem Last Slot) and ZBLL (Zborowski-Bruchem Last Layer)

After solving the F2L minus one corner-edge pair, ZBLS (a.k.a. ZBF2L) can be used to finish F2L while simultaneously orienting the edges of the last layer, and ZBLL, to finish the last layer in one algorithm. These sets have a total of 795 (302+493) algorithms. Many speedcubers learn a small subset of ZBLL and ZBLS is rarely used.

## Resources

- [Feliks Zemdegs](https://www.youtube.com/channel/UCQ7ASM-o1ELUbScXCKYwovA) ([CubeSkills](https://cubeskills.com/), [YouTube](https://www.youtube.com/channel/UCPftX98kFgj-fPk3pombPIg)): An excellent library of speedsolving tutorials and example walkthroughs from Feliks.
- [J Perm](https://www.youtube.com/channel/UCqTVfT9JQqhA6_Hi_h_h97Q) (See [featured channels](https://www.youtube.com/c/JPerm/channels))
- [badmephisto](https://www.youtube.com/channel/UCqgMq1Um_xnD45wTLPlvW7A)
- [Lucas Garron](https://garron.net/)
- [Jessica Fridrich's speedcubing page](http://www.ws.binghamton.edu/fridrich/cube.html)
- [Andy Klise's Rubik's Cube guides](http://www.kungfoomanchu.com/)
- [Speedsolving Wiki](https://www.speedsolving.com/wiki/index.php/Main_Page)
- [Ruwix - Twisty Puzzle Wiki](https://ruwix.com/)
- [Speedcubing - Wikipedia](https://en.wikipedia.org/wiki/Speedcubing)
- [The Speed Cubers - Netflix](https://www.netflix.com/title/81092143)
- [Why We Cube - A Speedcubing Documentary](https://www.youtube.com/watch?v=1oZY2e25VUw)
- [Cracking the Cube](https://www.goodreads.com/book/show/29430754-cracking-the-cube) - Ian Scheffler
- [r/Cubers Wiki](https://www.reddit.com/r/Cubers/wiki/index)
- [csTimer - Professional Rubik's Cube Speedsolving/Training Timer](https://cstimer.net/) ([Source](https://github.com/cs0x7f/cstimer))

I have compiled my favorite **_YouCubers_** and resources for algorithms, walkthroughs and tutorials on Rubik's Cube and other puzzles <Link href="/links/puzzles">here</Link>.

### Mathematics of the Rubik's Cube

If you are curious to learn about the mathematics of the Rubik's Cube, here are some resources that you might find interesting:

- [Mathematics of the Rubik’s cube - W. D. Joyner (1996)](http://www.permutationpuzzles.org/rubik/webnotes/rubik.pdf)
- [The Mathematics of the Rubik's Cube: Introduction to Group Theory and Permutation Puzzles - MIT (2009)](https://web.mit.edu/sp.268/www/rubik.pdf)
- [The Mathematics of the Rubik's Cube - UC Berkeley (2011)](https://math.berkeley.edu/~hutching/rubik.pdf)
- [Optimal solutions for Rubik's Cube](https://en.wikipedia.org/wiki/Optimal_solutions_for_Rubik%27s_Cube)
- [God's Algorithm](https://en.wikipedia.org/wiki/God%27s_algorithm)

Have fun and happy learning!

[^1]: As of 2020, the world record average for 3x3x3 is 5.53 seconds held by Australian speedcuber **Feliks Zemdegs** set in 2019.
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Deploying mdBook site on Netlify]]></title>
            <link>https://rspk.org/weblog/mdbook-netlify</link>
            <guid>https://rspk.org/weblog/mdbook-netlify</guid>
            <pubDate>Wed, 29 Jul 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Deploying mdBook site on Netlify]]></description>
            <content:encoded><![CDATA[
Now that I've got a domain, I have moved my project sites from GitHub Pages to Netlify, and serve them from subdomains right from Netlify using Netlify DNS. This is technically possible for sites hosted on GitHub Pages. You need to add a CNAME record to your DNS provider and CNAME file at the root of your project repository. Refer to [GitHub's documentation](https://docs.github.com/en/github/working-with-github-pages/managing-a-custom-domain-for-your-github-pages-site) for details.

This is a simple enough task but I came across some quirks and information that might be useful:

- One of my sites is built using [mdBook](https://github.com/rust-lang/mdBook), a static site generator written in Rust but Netlify doesn't support Rust in its [build image](https://github.com/netlify/build-image) like Node, Python or Ruby. If you use `mdbook build` as build command, you'll get `mdbook : command not found` error in deploy log.

To solve this problem, use the following build command:

```shell
curl -L https://github.com/rust-lang/mdBook/releases/download/v0.3.7/mdbook-v0.3.7-x86_64-unknown-linux-gnu.tar.gz | tar xvz && ./mdbook build
```

Make sure to change the version to the one that you want to use.

- mdBook puts generated HTML and CSS files in the `book` directory (like `build` dir used by npm) by default. So, use this dir (or whatever folder you have used in the configuration) as publish directory.

- I had problems with pages generated from **LICENSE** and **CONTRIBUTING.md** files. These files are located at the root directory of the project. I had to disable Netlify's [Pretty URLs](https://docs.netlify.com/site-deploys/post-processing/#post-processing-features) (**Settings > Build & deploy > Post processing > Asset optimization**) to make this work.

- I couldn't get sidebar to resize. Disabling **Bundle CSS** on Netlify worked. This might not be necessary if using v0.4.
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Targeting and styling external links using CSS]]></title>
            <link>https://rspk.org/weblog/external-links</link>
            <guid>https://rspk.org/weblog/external-links</guid>
            <pubDate>Tue, 21 Jul 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Targeting and styling external links using CSS]]></description>
            <content:encoded><![CDATA[
Here is the CSS snippet I use to target and show specific icons for [various](https://wikipedia.org) [external](https://github.com) [links](https://example.org) on this site. To use it, first save the external link and other brand icon SVGs in the `assets` directory and then set the `--color-icon` CSS variable somewhere.

```css showLineNumbers
/* Ignore URLs on this domain, internal links, anchors, and links with no-icon class */
a:not([href^='https://rspk.org']):not([href^='/']):not([href^='#']):not(
    .no-icon
  )::after {
  background-color: var(--color-icon);
  content: '';
  display: inline-flex;
  width: 10px;
  height: 10px;
  margin-left: 3px;
  margin-right: 1px;
  -webkit-mask-image: url(/assets/external-links/external-link.svg);
  mask-image: url(/assets/external-links/external-link.svg);
  -webkit-mask-size: cover;
  mask-size: cover;
}
a::after {
  font-size: 8px;
  padding: 0 2.25px;
  position: relative;
  bottom: 1.5px;
}
a[href*='wikipedia.org']::after {
  -webkit-mask-image: url(/assets/external-links/wikipedia.svg) !important;
  mask-image: url(/assets/external-links/wikipedia.svg) !important;
}
a[href*='github.com']::after {
  -webkit-mask-image: url(/assets/external-links/github.svg) !important;
  mask-image: url(/assets/external-links/github.svg) !important;
}
```

You'll also notice some links do a <a href="https://example.org/" target="_blank" rel="noopener noreferrer">bumpy animation</a> thing on hover. When clicked, they open in a new tab. These are links with `target` attribute set to `_blank`. Here is how to do it:

```css showLineNumbers
a[target^='_blank']:hover::after {
  position: relative;
  bottom: 0.3rem;
  transition: 0.2s ease-out;
}
```

## Using Font Awesome

I used the following snippet previously when using SCSS with Font Awesome icons.

```scss showLineNumbers
a {
  /* ... link styles */

  /* Target external links
     If you want to indicate websites with specific icons, make sure you add them here
     If using Font Awesome icons, font-family and font-weight need to be set based on
     whether you are using Brand or Regular icons
     https://fontawesome.com/v5.0.13/how-to-use/on-the-web/advanced/css-pseudo-elements

     Ignore internal links and specified domains (we'll style these later using brand icons) */

  &:not([href^='/']):not([href^='#']):not([href*='wikipedia.org']):not([href*='github.com'])::after {
    font-family: 'Font Awesome 5 Free';
    font-weight: 900;
    content: '\f35d'; /* external link icon unicode */
  }

  &::after {
    font-family: 'Font Awesome 5 Brands';
    font-weight: 400;
    color: rgb(85, 78, 78);
    font-size: 0.7rem;
    padding: 0.15rem;
    position: relative;
    bottom: 0.2rem;
  }

  /* Now, add individual icons from Font Awesome
     https://fontawesome.com/cheatsheet */
  &[href*='wikipedia.org']::after {
    content: '\f266';
  }

  &[href*='github.com']::after {
    content: '\f09b';
  }
}
```

## Further reading

- [CSS-Tricks - Target Only External Links](https://css-tricks.com/snippets/jquery/target-only-external-links/)
- [w3.org - Substring matching attribute selectors](https://www.w3.org/TR/selectors/#attribute-substrings)
- [MDN - Attribute selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors)
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Implementing /projects page on Gatsby site with GitHub API]]></title>
            <link>https://rspk.org/weblog/projects-github-api</link>
            <guid>https://rspk.org/weblog/projects-github-api</guid>
            <pubDate>Sun, 12 Jul 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Implementing /projects page on Gatsby site with GitHub API]]></description>
            <content:encoded><![CDATA[
On <Link href='/projects'>/projects</Link>, I am using GitHub's REST API to populate the stars count, forks count, description and homepage for my projects hosted on GitHub. Let's see how!

We'll be using functional components and as such React's `useEffect` and `useState` Hooks. Some other things you should be familiar with are template strings, `async/await`, destructuring and `try/catch`.

First, create a file named `github-api.js` in `src/services` directory and copy the following code into the file:

```js {10-14} showLineNumbers
import axios from 'axios'

const GITHUB_USERNAME = '<YOUR_GITHUB_USERNAME>'
const baseUrl = 'https://api.github.com/'

export const fetchData = async (project) => {
  try {
    const {
      data: { description, homepage, stargazers_count, forks_count },
    } = await axios({
      method: 'get',
      url: `${baseUrl}repos/${GITHUB_USERNAME}/${project.repo}`,
      timeout: 3000,
    })
    return {
      description,
      homepage,
      starsCount: stargazers_count,
      forksCount: forks_count,
    }
  } catch (error) {
    return project
  }
}
```

We are using [axios](https://github.com/axios/axios) to make requests. We could also use the native `fetch` API for this but `axios` makes it very easy to set timeout, cancel requests and set authorization headers if we need to authenticate with an access token. GitHub API requires a token to make 60+ requests per hour.

The asynchronous function `fetchData` takes in a `project` object as an argument and calls the API, then returns an object with `description`, `homepage`, `starsCount`, `forksCount` properties. If the request fails, the function will return the initial value for `project` that we can use as a fallback.

We will import the `fetchData` function in our component and call it inside the useEffect hook.

Create a component in `src/components` called `github-stats.js` and put the following code inside:

```jsx showLineNumbers
import React, { useState, useEffect } from 'react'
import { fetchData } from '../services/github-api'

const GITHUB_USERNAME = '<YOUR_GITHUB_USERNAME>'

const GitHubStats = ({ project }) => {
  const [starsCount, setStarsCount] = useState(project.starsCount)
  const [forksCount, setForksCount] = useState(project.forksCount)
  const [description, setDescription] = useState(project.description)
  const [homepage, setHomepage] = useState(project.homepage)

  const stargazersUrl = `https://github.com/${GITHUB_USERNAME}/${project.repo}/stargazers`
  const forksUrl = `https://github.com/${GITHUB_USERNAME}/${project.repo}/network/members`

  useEffect(() => {
    let isMounted = true

    const runEffect = async () => {
      const res = await fetchData(project)
      if (!isMounted) return

      setDescription(res.description)
      setHomepage(res.homepage)
      setStarsCount(res.starsCount)
      setForksCount(res.forksCount)
    }

    runEffect()
    
    return () => {
      isMounted = false
    }
  }, [project])

  return (
    <>
      <a href={`https://github.com/${GITHUB_USERNAME}/${project.repo}`}>
        {project.repo}
      </a>
      : {description}
      <br />
      {homepage && (<>Homepage: <a href={homepage}>{homepage}</a></>)}
      <br />
      Stars: <a href={stargazersUrl}>{starsCount}</a> &bull; Forks: <a href={forksUrl}>{forksCount}</a>
    </>
  )
}

export default GitHubStats
```

The GitHubStats component takes a `project` object as a prop and calls the fetchData function which returns an object with `description`, `homepage`, `starsCount` and `forksCount` keys. On a succesfull API call, these keys will be populated with latest data from GitHub. If the API call fails, these values will contain values from the `project` object that we pass.

Now, we can import the `GitHubStats` component in `src/pages/projects.js`:

```jsx showLineNumbers
import React from 'react'

import Layout from '../components/layout'
import GitHubStats from '../components/github-stats'

const projects = [
  {
    repo: 'project-one',
    description: 'Project One description.',
    homepage: 'https://example.org/1',
    starsCount: 10,
    forksCount: 5,
  },
  {
    repo: 'project-two',
    description: 'Project Two description.',
    homepage: 'https://example.org/2',
    starsCount: 5,
    forksCount: 2,
  },
]

const ProjectsPage = () => {
  return (
    <Layout>
      <ul>
        {projects.map((project) => (
          <li key={project.repo}>
            <GitHubStats project={project} />
          </li>
        ))}
      </ul>
    </Layout>
  )
}

export default ProjectsPage
```

We create an array of objects with backup data to use if API call fails. Then, we map through each project and render `GitHubStats` component passing in the `project` as prop. And that's it!

Some things you could now try might be styling the page, using an object to store the state instead of multiple useState calls, or separating the backup data into the `src/data/` directory for better [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns).
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A privacy focused note-taking and PKM setup]]></title>
            <link>https://rspk.org/weblog/note-taking</link>
            <guid>https://rspk.org/weblog/note-taking</guid>
            <pubDate>Wed, 17 Jun 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[A privacy focused note-taking and PKM setup]]></description>
            <content:encoded><![CDATA[
With an overwhelmingly increasing number of productivity tools and note-taking applications on the market, it has become increasingly difficult to settle on one particular application/workflow for taking notes. Over the years, I've experimented with a wide range of setups, from pen and paper to completely offline solutions like txt files, [VimWiki](https://github.com/vimwiki/vimwiki), [Zettlr](https://github.com/zettlr/zettlr) and [MarkText](https://github.com/marktext/marktext), [Joplin](https://github.com/laurent22/joplin) and SaaS products like [Roam](https://roamresearch.com/) and methods like [Zettelkasten](https://en.wikipedia.org/wiki/Zettelkasten) (which didn't work for my programming notes). For the past few months, however, I've settled on a simple workflow that I'll explain below.

First, let's look at the high level overview of what my notes consist of (yet). There are typically 3 kinds of notes that I take:

- Links (websites, go-to guides/articles/blog posts/lists online)
- Snippets of code or information (notes from books: programming + non-fiction, courses, MOOCs, mathematics, pieces of objective information)
- Temporary stuff (project ideas, short term todos, grocery list, scratchpad, etc)

For all types of notes, I like to use a lightweight markup language, namely Markdown, except math, where I use LaTeX/KaTeX. Markdown is supported by all major static site generators which makes it convenient to publish my notes to the web and learning Markdown takes virtually no time. Many static site generators now support MDX, an advanced version of Markdown that allows importing React components inside markdown files, KaTeX support, and other advanced extensions.

Secondly, here are the features I'm looking for in my workflow:

- I can edit notes quickly, ideally within a tmux pane on my terminal using Vim.
- I can sync and edit all my data across devices.
- My private notes must be end-to-end encrypted if on cloud.

## Links

### Public links

I like collecting websites. As of now, there are more than 5000 websites in [my collection](https://github.com/rsapkf/42/).

For this, I have a bunch of markdown files in a folder with filenames matching the top level category that each website goes into. Inside each of those files, there are sub-categories and nested hierarchies of lists with more and more specific criteria for categorization.

This isn't an attempt to create another [alternativeTo.net](https://alternativeto.net) or one of those [awesome lists](https://github.com/topics/awesome). Just a personal thing.

I have a few bookmarklets, vim macros, and scripts that do the formatting for me. I wrote an addon that recursively exports a specific folder from my bookmarks on Firefox to properly formatted Markdown (**Sidenote**: I am working on publishing this addon with added functionality) and copy those links into whatever files they should go to. While browsing, I drag new websites to that folder on my Bookmarks Toolbar and at the end of the week, load the extension and export the data. Then, I push the updates to GitHub and use [Travis](https://travis-ci.org/) to build the [website](https://wiki.rsapkf.org) (I use [mdBook](https://github.com/rust-lang/mdBook) for this) and deployment is done via [GitHub Pages](https://pages.github.com).

_Update: I have moved to Netlify for CI/CD._

### Personal links

I have always been a [Firefox](https://www.mozilla.org/en-US/firefox/new/) user. I use [Firefox Developer Edition](https://www.mozilla.org/en-US/firefox/developer/) as my primary browser. For links that I'd rather not share with the world, I use [Firefox Sync](https://support.mozilla.org/en-US/products/firefox/sync). There are multiple folders and hierarchies that I personally use to sort my bookmarks. For example, I use it to store:

- YouTube playlists + custom playlists (as opposed to YouTube's "Save to Library" and "Liked videos" features),
- Reddit threads/memes (as opposed to Reddit's "Save" feature,
- IMDb links (instead of IMDb's "Lists"),
- Twitter posts (instead of Twitter's "Likes"),
- Bookmarklets,
- Interesting Instagram profiles (e.g. [@electricpants](https://www.instagram.com/electricpants/)),
- Spotify/YouTube Music links
- Resources that I should check later,
- Other temporary links, tutorials to follow, etc.

This approach might seem time-consuming and unnecessary but as a privacy conscious person, I find it to be the best solution that aligns with my personal threat model.


This way, I don't have to log into big corporate websites to view something. I can store my data in as many nested folders and tags as I can think of, encrypted and synced across all my devices. This helps me minimize my [digital footprint](https://en.wikipedia.org/wiki/Digital_footprint) and leaves less amount of [PII](https://en.wikipedia.org/wiki/Personal_data) tied to big companies.

To export all my bookmarks to Markdown, I can use Firefox's Export Bookmarks to HTML feature and use a tool like [pandoc](https://github.com/jgm/pandoc) to convert HTML to Markdown.

Also, I can self-host an instance of [Firefox Sync Server](https://github.com/mozilla-services/syncserver) on my own server if I want to. There are [other](https://github.com/go-shiori/shiori) [alternatives](https://github.com/awesome-selfhosted/awesome-selfhosted#bookmarks-and-link-sharing) to Firefox Sync, but for now, Firefox is good enough for me.

For links that I want to check out later on my laptop while on my phone, I use [Pocket](https://getpocket.com) (This isn't open source, but it's owned by Mozilla and these folks seem to be more trustworthy than most other companies). I am thinking of eventually switching to a self-hosted alternative like [wallabag](https://github.com/wallabag/wallabag). Some useful features of Pocket are Tagging and [Export](https://getpocket.com/export/) tool which allows me to import the links to Firefox with ease.

I also use Pocket as an alternative to YouTube's "Watch Later" and IMDb's "Watchlist" features.

_Update: I have deleted Pocket and moved to Firefox for temporary links as well._

## Notes

These consist of information I extract from reading (non-fiction) books, articles, watching YouTube videos, quotes, etc.

### Public notes

Again, I just have a folder with a bunch of Markdown files that I write my notes in. I currently publish these notes [here](https://wiki.rsapkf.org) and use [Docusaurus](https://github.com/facebook/docusaurus) for generating the website.

These notes consist small snippets of code, shell scripting and programming tips, Linux commands that I frequently use, IRC commands that I tend to forget all the time, etc. Essentially, these are little pieces of random information for personal use that aren't tied to me personally or summaries of books I read and that I think someone will benefit from reading.

### Private notes

This is where my most personal data and notes reside. I use an open-source note-taking service called [Standard Notes](https://github.com/standardnotes/) for this. This provides automatic end-to-end encryption for my notes, free sync across my devices and the "Extended" tier has some nice features like code editor, vim mode, spreadsheet, etc. I open [Standard Notes web](https://github.com/standardnotes/web) in a new browser window when I am reading an information-dense piece on the internet or non-fiction books. It also has clients for Linux and other OSes. The free tier has all the core features. It is privacy focused, lightweight, has a simple intuitive interface and is a breeze to write my notes on.

On my phone, I use Standard Notes for both public and private notes that I can later sort out on my computer.

I use LaTeX and Neovim + [UltiSnips](https://github.com/sirver/UltiSnips) + [Inskcape](https://gitlab.com/inkscape/inkscape) workflow from [Gilles Castel](https://castel.dev/post/lecture-notes-1/) for mathematics notes.

I manage highly sensitive information like recovery codes, paper keys, API keys, etc with [KeePassXC](https://keepassxc.org/) - my password manager.

### Dotfiles and blog

I try to document everything on my config files so I can understand what each line of configuration is doing. I store these on [GitHub](https://github.com/rsapkf/config) and manage them with a [technique](https://news.ycombinator.com/item?id=11071754) I learned from HN.

For this blog, I use [Gatsby](https://github.com/gatsbyjs/gatsby) with ~~[gatsby-transformer-remark](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-transformer-remark)~~ [gatsby-plugin-mdx](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-mdx) and [gatsby-source-filesystem](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-source-filesystem) plugins, among others, that allow me to read Markdown files from the filesystem and generate HTML pages. You can read more details on this <Link href="/about#colophon">here</Link>.

_Update: I have moved my blog over to Next.js._

## Temporary todos and tasks

I use Standard Notes for these as well. A better solution for more ephemeral notes and todos would be something like [Tasks.org](https://github.com/tasks/tasks) or [todo.txt](https://github.com/todotxt/todo.txt-cli) but this just adds one more application to my setup. For temporary grocery list and stuff, I use the default Notes app that comes with my phone (in offline mode) and for long-term todos, I use Standard Notes.

## tl;dr

In a nutshell, most of my notes are stored in [Markdown](https://daringfireball.net/projects/markdown/syntax) files inside folders on my hard drive. I use [Neovim](https://github.com/neovim/neovim) to edit these notes and [Git](https://git-scm.com/) for version control and sync. I share [some](https://github.com/rsapkf/42/) [of](https://github.com/rsapkf/config) [them](https://github.com/rsapkf/www) with the world. For private notes, I use end-to-end encrypted services: [Firefox](https://www.mozilla.org/en-US/firefox/), [Standard Notes](https://github.com/standardnotes/), and [KeePassXC](https://keepassxc.org). I use [Firefox Sync](https://support.mozilla.org/en-US/products/firefox/sync) to save and sync links that I want to read later.
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Making OLL and PLL visualizations using VisualCube and Python]]></title>
            <link>https://rspk.org/weblog/visualcube</link>
            <guid>https://rspk.org/weblog/visualcube</guid>
            <pubDate>Wed, 03 Jun 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Making OLL and PLL visualizations using VisualCube and Python]]></description>
            <content:encoded><![CDATA[
[VisualCube](http://cube.rider.biz/visualcube.php) ([Source](https://github.com/Cride5/visualcube)) is a PHP script that allows you to make visualizations for the Rubik's Cube for demonstration purposes. Well-known speedcubing guides use this tool. I wanted to download all OLL and PLL cases in SVG and PNG formats for my <Link href="/weblog/pll-recognition">PLL recognition guide</Link>. Here's how I did it using few short Python and JavaScript scripts.

VisualCube allows you to create several types of visuals by changing transparency, masking pieces that are not related to a specific step in CFOP, tweaking the angle of the cube, etc. All these variables can be specified using URL query parameters. For PLL, the URL I wanted was:

```plaintext
http://cube.rider.biz/visualcube.php?fmt=svg&pzl=3&view=plan&case=<algorithm>
```

The parameters work like this:

- `fmt`: Specify the type of image extension.
- `pzl`: Specify the puzzle type (e.g. 3 for 3x3, 4 for 4x4, etc).
- `view`: Specify special view for the image. `plan` rotates the cube to expose the U face which is what we want for PLL.
- `case`: Specify the alg which **solves** a specific case.

[Example for H perm](http://cube.rider.biz/visualcube.php?fmt=svg&pzl=3&view=plan&case=M2UM2U2M2UM2)<br />
[Example for OLL 45](http://cube.rider.biz/visualcube.php?fmt=svg&pzl=3&view=plan&stage=oll&case=FRUR'U'F')

The first 3 parts of the URL are static. What I want is a list of all PLL algorithms and use them one by one to form a URL and then download the page to my local disk.

## PLL

For the PLL algorithms, I visited [AlgDb.net's PLL page](http://algdb.net/puzzle/333/pll), opened the developer console (CTRL+Shift+I), and ran this JavaScript code in Firefox's multi-line editor mode:

```javascript showLineNumbers
let algs = ''

document.querySelectorAll('.case-img').forEach((img) => {
  let params = new URLSearchParams(img.src)
  // Get the value of case parameter from URL and remove all spaces
  // I found that AlgDb uses its own hosted version of VisualCube with a similar API for images
  let alg = params.get('case').split(' ').join('')
  // Extract the PLL case name from parent's href attribute
  let pll = img.parentNode.getAttribute('href').replace('/puzzle/333/pll/', '')
  // Capitalize the first letter of each PLL
  let capPll = pll.charAt(0).toUpperCase() + pll.slice(1)
  algs += `"${capPll}" : "${alg}",\n`
})

console.log(`plls = {\n${algs}}`)
```

This outputs a nicely formatted Python dictionary with all PLL names as keys and associated algorithms as values in the console which can be copied and pasted to a Python script.

## OLL

Similarly, for OLLs, I visited [AlgDb's OLL page](http://algdb.net/puzzle/333/oll) and ran this code in the console:

```javascript showLineNumbers
let algs = ''
// Since OLLs cases are just numbers, we can use this variable and increment it
let ollCase = 1

document.querySelectorAll('.case-img').forEach((img) => {
  let params = new URLSearchParams(img.src)
  let alg = params.get('case').split(' ').join('')
  algs += `"${ollCase}" : "${alg}",\n`
  ollCase++
})

console.log(`olls = {\n${algs}}`)
```

The following Python script iterates through these dicts to get the images and saves them locally as **\<oll_number\>.svg** and **\<pll_name\>.svg** respectively in separate directories. Notice the `oll_url` has an additional parameter called `stage` equal to `oll` so that irrelevant stickers are masked from being colored and makes the result clearer. Just replace the occurences of `svg` in the file to `png` and run the script again to get all images as PNG files.

Make sure to run this from an empty directory. Note that these algorithms may not be the best ones in terms of fingertricks and efficiency. In some algs, y/y'/y2 moves have been added at the beginning in order to output images at an angle in which I apply certain algs.

```python showLineNumbers
import os
import urllib.request

oll_url = 'http://cube.rider.biz/visualcube.php?fmt=svg&pzl=3&view=plan&stage=oll&case='
pll_url = 'http://cube.rider.biz/visualcube.php?fmt=svg&pzl=3&view=plan&case='
plls = {
    "Aa": "R'FR'B2RF'R'B2R2",
    "Ab": "l'R'D2RUR'D2RU'Rx'",
    "E": "yLR'U'RUL'U'R'URrUR'U'r'FRF'",
    "F": "y'R'URU'R2F'U'FURFR'F'R2",
    "Ga": "R2uR'UR'U'Ru'R2y'R'UR",
    "Gb": "R'U'RyR2uR'URU'Ru'R2",
    "Gc": "R2u'RU'RUR'uR2yRU'R'",
    "Gd": "RUR'y'R2u'RU'R'UR'uR2",
    "H": "M2UM2U2M2UM2",
    "Ja": "y'L'U2LUL'U2RU'LUR'",
    "Jb": "RUR'F'RUR'U'R'FR2U'R'U'",
    "Na": "LU'RU2L'UR'LU'RU2L'UR'",
    "Nb": "R'UL'U2RU'LR'UL'U2RU'L",
    "Ra": "y'LU2L'U2LF'L'U'LULFL2",
    "Rb": "R'U2RU2R'FRUR'U'R'F'R2",
    "T": "RUR'U'R'FR2U'R'U'RUR'F'",
    "Ua": "y2R2U'R'U'RURURU'R",
    "Ub": "y2R'UR'U'R'U'R'URUR2",
    "V": "R'UR'd'R'F'R2U'R'UR'FRF",
    "Y": "FRU'R'U'RUR'F'RUR'U'R'FRF'",
    "Z": "M2UM2UM'U2M2U2M'",
}

olls = {
    "1": "RU2R2'FRF'U2R'FRF'",
    "2": "FRUR'U'F'fRUR'U'f'",
    "3": "yr'R2UR'UrU2r'UM'",
    "4": "yMU'rU2r'U'RU'R2r",
    "5": "r'U2RUR'Ur",
    "6": "rU2R'U'RU'r'",
    "7": "rUR'URU2r'",
    "8": "y2l'U'LU'L'U2l",
    "9": "y'RU2R'M'U'RU'R'UM",
    "10": "RUR'yR'FRU'R'F'R",
    "11": "r'R2UR'URU2R'UM'",
    "12": "yFRUR'U'F'UFRUR'U'F'",
    "13": "FURU'R2F'RURU'R'",
    "14": "R'FRUR'F'Ry'RU'R'",
    "15": "r'U'rR'U'RUr'Ur",
    "16": "rUr'RUR'U'rU'r'",
    "17": "RUR'UR'FRF'U2R'FRF'",
    "18": "l'U'r'U2LU2L'U2rUl",
    "19": "r'RURUR'U'rR2'FRF'",
    "20": "MURUR'U'M2URU'r'",
    "21": "y'RUR'URU'R'URU2R'",
    "22": "RU2R2U'R2U'R2U2R",
    "23": "R2DR'U2RD'R'U2R'",
    "24": "rUR'U'r'FRF'",
    "25": "R'FRB'R'F'RB",
    "26": "y'R'U'RU'R'U2R",
    "27": "RUR'URU2R'",
    "28": "rUR'U'MURU'R'",
    "29": "MURUR'U'R'FRF'M'",
    "30": "yr'U2RUR'UrRU2R'U'RU'R'",
    "31": "R'U'FURU'R'F'R",
    "32": "RUB'U'R'URBR'",
    "33": "RUR'U'R'FRF'",
    "34": "y2FRUR'U'R'F'rURU'r'",
    "35": "RU2R2'FRF'RU2R'",
    "36": "R'U'RU'R'URURyR'F'R",
    "37": "FR'F'RURU'R'",
    "38": "RUR'URU'R'U'R'FRF'",
    "39": "y'R'r'D'rU'r'DrUR",
    "40": "y'RrDr'UrD'r'U'R'",
    "41": "y2RU'R'U2RUyRU'R'U'F'",
    "42": "R'U'RU'R'U2RFRUR'U'F'",
    "43": "f'L'U'LUf",
    "44": "fRUR'U'f'",
    "45": "FRUR'U'F'",
    "46": "R'U'R'FRF'UR",
    "47": "F'L'U'LUL'U'LUF",
    "48": "FRUR'U'RUR'U'F'",
    "49": "y2RB'R2FR2BR2F'R",
    "50": "r'Ur2U'r2'U'r2Ur'",
    "51": "fRUR'U'RUR'U'f'",
    "52": "RUR'URd'RU'R'F'",
    "53": "r'U'RU'R'URU'R'U2r",
    "54": "rUR'URU'R'URU2r'",
    "55": "RU2R2U'RU'R'U2FRF'",
    "56": "rUr'URU'R'URU'R'rU'r'",
    "57": "RUR'U'rR'URU'r'",
}

os.makedirs('oll/svg', exist_ok=True)
os.makedirs('pll/svg', exist_ok=True)

def download_vis(stage, url, algs_dict):
    for case, alg in algs_dict.items():
        urllib.request.urlretrieve(
            f'{url}{alg}', f'{stage}/svg/{case}.svg')

download_vis('oll', oll_url, olls)
download_vis('pll', pll_url, plls)
```

## PLL cases with arrows

With VisualCube, it is also possible to make arrows for PLL cases. Additional parameters required for this are:

- `ac`: Default arrow color
- `arw`: Comma separated list of arrows to draw. Add `-s[0-9]` at the end to adjust the length of arrows. Check the following script to see how this works.

```python showLineNumbers
import os
import urllib.request

pll_arrows_url = 'http://cube.rider.biz/visualcube.php?fmt=svg&pzl=3&view=plan&ac=black&case='
plls = {
    "Aa": "R'FR'B2RF'R'B2R2&arw=U0U2-s8,U2U8-s8,U8U0-s8",
    "Ab": "l'R'D2RUR'D2RU'Rx'&arw=U8U2-s8,U0U8-s8,U2U0-s8",
    "E": "yLR'U'RUL'U'R'URrUR'U'r'FRF'&arw=U0U6,U6U0,U2U8,U8U2",
    "F": "y'R'URU'R2F'U'FURFR'F'R2&arw=U2U8,U8U2,U1U7,U7U1",
    "Ga": "R2uR'UR'U'Ru'R2y'R'UR&arw=U0U2-s8,U2U6-s8,U6U0-s8,U1U3-s7,U3U5-s7,U5U1-s7",
    "Gb": "R'U'RyR2uR'URU'Ru'R2&arw=U0U6-s8,U6U8-s8,U8U0-s8,U1U7-s7,U7U3-s7,U3U1-s7",
    "Gc": "R2u'RU'RUR'uR2yRU'R'&arw=U0U6-s8,U6U8-s8,U8U0-s8,U7U3-s7,U3U5-s7,U5U7-s7",
    "Gd": "RUR'y'R2u'RU'R'UR'uR2&arw=U0U2-s8,U2U6-s8,U6U0-s8,U1U3-s7,U3U7-s7,U7U1-s7",
    "H": "M2UM2U2M2UM2&arw=U1U7,U7U1,U5U3,U3U5",
    "Ja": "y'L'U2LUL'U2RU'LUR'&arw=U0U2,U2U0,U3U1,U1U3",
    "Jb": "RUR'F'RUR'U'R'FR2U'R'U'&arw=U2U8,U8U2,U5U7,U7U5",
    "Na": "LU'RU2L'UR'LU'RU2L'UR'&arw=U1U7,U7U1,U0U8,U8U0",
    "Nb": "R'UL'U2RU'LR'UL'U2RU'L&arw=U1U7,U7U1,U6U2,U2U6",
    "Ra": "y'LU2L'U2LF'L'U'LULFL2&arw=U1U3,U3U1,U2U8,U8U2",
    "Rb": "R'U2RU2R'FRUR'U'R'F'R2&arw=U0U2,U2U0,U5U7,U7U5",
    "T": "RUR'U'R'FR2U'R'U'RUR'F'&arw=U3U5-s8,U5U3-s8,U2U8,U8U2",
    "Ua": "y2R2U'R'U'RURURU'R&arw=U5U3-s7,U3U7-s7,U7U5-s7",
    "Ub": "y2R'UR'U'R'U'R'URUR2&arw=U3U5-s7,U5U7-s7,U7U3-s7",
    "V": "R'UR'd'R'F'R2U'R'UR'FRF&arw=U1U5,U5U1,U0U8,U8U0",
    "Y": "FRU'R'U'RUR'F'RUR'U'R'FRF'&arw=U1U3,U3U1,U0U8,U8U0",
    "Z": "M2UM2UM'U2M2U2M'&arw=U1U3,U3U1,U5U7,U7U5",
}

os.makedirs('pll-arrows/svg', exist_ok=True)

for case, alg in plls.items():
    urllib.request.urlretrieve(
        f'{pll_arrows_url}{alg}', f'pll-arrows/svg/{case}.svg')
```
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Beating 2048]]></title>
            <link>https://rspk.org/weblog/2048</link>
            <guid>https://rspk.org/weblog/2048</guid>
            <pubDate>Mon, 01 Jun 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Beating 2048]]></description>
            <content:encoded><![CDATA[
## The only strategies you need to learn

- Put the highest tile in a corner. I prefer choosing the top left corner but it's irrelevent to the overall strategy.

- Keep the row with the highest tile filled at all times.

- Only move your highest corner tile when forced to, then immediately return it.

  Now, there will be cases when the corner will be replaced with a small tile but that doesn't mean it's the end of the game. It will just make your position a little weaker and decrease your chances of getting a very high score. Nevertheless, getting a 2048 should not be a problem if you follow the above steps.

## Fun facts

- You can type in [this](https://duckduckgo.com/?q=2048) DuckDuckGo query to play 2048 in your browser!
- [Here](https://github.com/gabrielecirulli/2048) is the source code of the earliest implementation written by Gabriele Cirulli in 2014. This was based on its predecessor 1024. You can play the original version of the game at [play2048.co](https://play2048.co/).
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Enabling italics in Vim and tmux]]></title>
            <link>https://rspk.org/weblog/vim-tmux-italics</link>
            <guid>https://rspk.org/weblog/vim-tmux-italics</guid>
            <pubDate>Tue, 17 Mar 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Enabling italics in Vim and tmux]]></description>
            <content:encoded><![CDATA[
If you're like me, you fiddle around with your development setup and dotfiles frequently tweaking and breaking things and learning along the way.

I only recently found out that you could have comments on Vim appear in _italics_. All you need to do is add `highlight Comment cterm=italic` to your `.vimrc`. Except, it's usually not that straightforward! I spent 3.5 hours researching how to properly enable italics in Vim and then, in Vim inside [tmux](https://github.com/tmux/tmux) and found out some genuine and a few stupid reasons things might not work as expected on your system.

Here, I will provide you official instructions to solving this problem and things you should care about if they don't work.

## Italics in Vim

Before jumping right in, I think it's a good idea to learn some basics of what is actually needed to render italics in your terminal.

- ### Environment variables

  An environment variable is a named object that contains data used by one or more applications. In simple terms, it is a variable with a name and a value. The value of an environmental variable can for example be the location of all executable files in the file system, the default editor that should be used, or the system locale settings. [_ArchWiki_](https://wiki.archlinux.org/index.php/Environment_variables)

- ### TERM

  TERM is a common environment variable used by a Linux system that contains the type of the running terminal. It is used by programs running in the terminal that wish to use terminal-specific capabilities. To see the value of TERM environment variable in your system, run `echo $TERM`. In most cases, it will be something like `xterm-256color` if you're using a 256 color terminal.

- ### Terminfo

  Terminfo is a data base describing terminals, used by screen-oriented programs such as vi, and libraries such as curses. Terminfo describes terminals by giving a set of capabilities which they have, by specifying how to perform screen operations, and by specifying padding requirements and initialization sequences. [_linux.die.net_](https://linux.die.net/man/5/terminfo)

- ### `infocmp`

  `infocmp` can be used to compare or print terminfo descriptions.

- ### `tic`/`toe`

  `tic` is the terminfo entry-description compiler. This command translates the terminfo files from source format into compiled format. To view all terminfo entries on your system, type `toe`.

And now we can begin. To have italics enabled in terminal Vim/Neovim, you need to have a terminal emulator that supports italics. To check this for your terminal, type the following:

```shell
$ echo -e "\e[3m foo \e[23m"
```

If you see the output _`foo`_ (in italics), then congratulations! Your terminal can support italics. If you don't, I recommend you switch to a more [modern](https://github.com/jwilm/alacritty) [terminal](http://software.schmorp.de/pkg/rxvt-unicode.html) [emulator](https://st.suckless.org/).

If you don't see italicised _`foo`_ above, then you need to add a custom terminfo. Fortunately, there is [one](https://gist.github.com/sos4nt/3187620) that we can use. Create a file named `xterm-256color-italic.terminfo` and put these lines in it:

```plaintext
xterm-256color-italic|xterm with 256 colors and italic,
  sitm=\E[3m, ritm=\E[23m,
  use=xterm-256color,
```

Now, compile the `xterm-256color-italic.terminfo` file with `tic`:

```shell
$ tic xterm-256color-italic.terminfo
```

Lastly, load it in your `~/.bashrc` or `~/.zshrc`:

```shell
$ echo "export TERM=xterm-256color-italic" >> ~/.bashrc
# or
$ echo "export TERM=xterm-256color-italic" >> ~/.zshrc
```

Source your `.bashrc` (`source ~/.bashrc`), restart the terminal and try the test above to make sure your terminal now outputs the italicised _`foo`_.

Now, to have italicized comments in Vim, add this line to your `.vimrc` / `init.vim` **after you declare your colorscheme**:

```vim
highlight Comment cterm=italic
```

Save and open the file again. Did it work? Yes? Great. But if it didn't, restart your terminal and open the file again. This should work now.

If you still don't see italics, add these lines to your `.vimrc`:

```vim
set t_ZH=^[[3m
set t_ZR=^[[23m
```

These are special terminal options that enable italics mode and italics end respectively using proper escape sequences used by Vim to get information about the running terminal.

**Note**: The characters `^[` must be entered with `<Ctrl-v><Esc>`.

**Sidenote**: If you're using [jellybeans.vim](https://github.com/nanotech/jellybeans.vim#italics) colorscheme for Vim, put this line after `colorscheme jellybeans`:

```vim
let g:jellybeans_use_term_italics = 1

" For onedark.vim - https://github.com/joshdick/onedark.vim#options
let g:onedark_terminal_italics = 1
```

## Italics in Vim inside tmux

If you made it this far, chances are you are using a terminal multiplexer like tmux for managing multiple sessions in the terminal. But if you try the test above inside tmux, you'll notice italics don't work anymore. This is because tmux sets the environment variable `TERM` to `screen-256color` instead of `xterm-256color` and hence uses the italics escape sequence incorrectly. For tmux 2.1 and above, you'd have had to create a new terminfo entry:

```shell
$ cat <<EOF|tic -x -
tmux|tmux terminal multiplexer,
    ritm=\E[23m, rmso=\E[27m, sitm=\E[3m, smso=\E[7m, Ms@,
    use=xterm+tmux, use=screen,

tmux-256color|tmux with 256 colors,
    use=xterm+256setaf, use=tmux,
EOF
```

And then tell tmux to use it in `~/.tmux.conf`:

```shell
set -g default-terminal "tmux"
# or
set -g default-terminal "tmux-256color"
```

For more recent tmux versions, just adding the above line to `~/.tmux.conf` suffices. A new terminfo entry may not be required.

See [tmux FAQs](https://github.com/tmux/tmux/wiki/FAQ#i-dont-see-italics-or-italics-and-reverse-are-the-wrong-way-round) and [tmux 2.1 FAQs](https://github.com/tmux/tmux/blob/2.1/FAQ#L355-L383).

If you still don't see italics in tmux, **make sure** that you don't have any tmux sessions running on your system (`tmux kill-server`), then try again.

Also, make sure you have an italics capable font enabled. I have tested this on rxvt-unicode with [Source Code Pro](https://github.com/adobe-fonts/source-code-pro) and on Alacritty with [Mononoki Nerd Font](https://github.com/ryanoasis/nerd-fonts).

_<small>Italics in Vim + tmux + Alacritty + Mononoki Nerd Font</small>_ :
![Italics in Vim+tmux+urxvt](/img/weblog/vim-tmux-italics-alacritty-mononoki-nerd.png)
_<small>Italics in Vim + tmux + urxvt + Source Code Pro Font</small>_ :
![Italics in Vim+tmux+Alacritty](/img/weblog/vim-tmux-italics-urxvt-source-code-pro.png)

## Further reading

- [tmux FAQs](https://github.com/tmux/tmux/wiki/FAQ)

]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Managing Python virtual environments with venv module]]></title>
            <link>https://rspk.org/weblog/venv</link>
            <guid>https://rspk.org/weblog/venv</guid>
            <pubDate>Wed, 07 Aug 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[Managing Python virtual environments with venv module]]></description>
            <content:encoded><![CDATA[
Python is notorious for having a lot of different dependency management tools: virtualenv, virtualenvwrapper, pipenv, pyvenv, pipx, poetry, etc. But the `venv` module is a fairly new and easier to use tool that comes built-in with Python3.3+. To use it, just navigate to your project folder and do:

```shell
$ python3 -m venv ./venv
```

Here, the second 'venv' is the name of the virtual environment we want to create.

Now, the project structure looks like:

```shell
$ tree myproject -L 2
myproject
├── myproject
│   └── ...
└── venv
    ├── bin
    ├── include
    ├── lib
    └── pyvenv.cfg
```

To activate the virtual environment, just do:

```shell
$ source ./venv/bin/activate
```

Activating a virtualenv modifies your shell prompt showing the name of the virtualenv. This means that the Python interpreter and libraries installed into it are isolated from those installed in other virtual environments, and in the system Python, leaving the global environment unaffected:

```shell
(venv) $ python3 -m pip install django
(venv) $ python3 -m pip list
Django (2.2.6)
pip (9.0.1)
pytz (2019.3)
setuptools (39.0.1)
sqlparse (0.3.0)
...
(venv) $
```

Before activating a virtual environment, the `python` command maps to the system version of python interpreter:

```shell
$ which python3
/usr/bin/python3
```

But with an active virtual environment, the `python` command maps to the interpreter binary inside the active virtualenv:

```shell
$ (venv) which python3
/home/$USERNAME/myproject/venv/bin/python3
```

To deactivate the virtual environment:

```shell
(venv) $ deactivate
```

To export your dependencies to an external file:

```shell
(venv) $ python3 -m pip freeze > requirements.txt
```

Deleting the virtual environment is as simple as deleting the 'venv' directory:

```shell
$ rm -rf ./venv
```

Make sure to deactivate the virtualenv before deleting it.

## Further reading

- [Python docs - venv](https://docs.python.org/3/library/venv.html).
- [Stack Overflow discussion on the topic](https://stackoverflow.com/questions/41573587/what-is-the-difference-between-venv-pyvenv-pyenv-virtualenv-virtualenvwrappe).
- [Why you should use python -m pip](https://snarky.ca/why-you-should-use-python-m-pip/).
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A fruitful distraction]]></title>
            <link>https://rspk.org/weblog/wm</link>
            <guid>https://rspk.org/weblog/wm</guid>
            <pubDate>Mon, 20 May 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[A fruitful distraction]]></description>
            <content:encoded><![CDATA[
As someone deeply interested in programming, \*nix, open source and STEM in general, I have been into countless rabbitholes during my learning process. Allow me to share one of them with you.

After I switched entirely to Linux last year, I decided to learn C++ by following Stroustrup's [book](https://www.stroustrup.com/programming.html) and do the exercises in Vim to brush up on my Vim skills.

I found myself constantly switching between Vim, terminal, e-book viewer and my browser windows in order to solve the problems. As an Ubuntu Gnome newbie, I relied on `Alt+Tab` key combination to do so. Eventually, I realized I could do the same thing using `Super+Tab` keys! It was surprising to me because I had always used the former solution since my Windows days. Curious about which approach was more common among other Linux users, I did a quick search on the web.

I found a Reddit [thread](https://web.archive.org/web/20210122102916/https://old.reddit.com/r/AskProgramming/comments/9s4phf/linux_users_do_you_use_supertab_or_alttab_to/) where someone had asked the same question. The first comment on the thread said, "`Alt+Tab` because I didn't know `Super` could do that as well." Okay. But what proved to be the beginning of the biggest distraction of my life, was the second comment with 7 upvotes. It said, "I use i3wm, so none of those :p"

Now, as a voraciously curious person, I _had_ to do a quick search&mdash;I had never heard of i3wm before. As some of you might already know, it's a powerful tiling window manager. One look into its documentation, I knew I always needed something like this in order to do things effectively.

I found that the default look that `i3` comes with isn't very attractive. Not just its look, but I had to do a number of additions and modifications to its config file to make things work properly.

For example, it isn't very straightforward to set a wallpaper. You have to install and configure a separate little program called [feh](https://wiki.archlinux.org/index.php/Feh) in order to do so (mind you, there are [alternatives](https://wiki.archlinux.org/index.php/Nitrogen)). It took me 3 weeks to make it usable for my specific needs, which included i3-gaps, i3blocks, touchpad configuration, audio controls, fonts, colors, keybindings, and more. During this period, I stumbled upon the Arch Linux wiki, and that was it, I had discovered the holy grail of [unix configuration](https://www.reddit.com/r/unixporn/wiki/themeing/dictionary#wiki_rice).

![screenshot](/img/weblog/wm.png)

Eventually, I came across [r/unixporn](https://reddit.com/r/unixporn), [Luke Smith](https://www.youtube.com/channel/UC2eYFnH61tmytImy1mTYvhA), [DistroTube](https://www.youtube.com/channel/UCVls1GmFKf6WlTraIb_IaJg), etc. I watched several [i3wm](https://www.youtube.com/playlist?list=PL5ze0DjYv5DbCv9vNEzFmP6sU7ZmkGzcf) and [urxvt](https://www.youtube.com/playlist?list=PL5ze0DjYv5DYf93rdfDWBRKdwps_-oKnI) tutorials from Code Cast on YouTube. As a result, I was able to customize my computer to suit my preferences, resulting in a unique look. Additionally, I created a [repository](https://github.com/rsapkf/config) on Github to backup and store my dotfiles in case I ever decide to change my distro. I became another 'btw, I use Arch' guy, constantly configuring my system, and discovering new tools and open source software programs all the time, but I haven't yet revisited the C++ book. I anticipate resuming it in about a week or so but there is no denying that that seemingly stupid question has taught me so much.

If this sounds interesting to you, I recommend trying out a window manager first. Because of the excellent documentation that it has, my personal suggestion is to go for [i3](https://i3wm.org) first. If you like minimalism and don't mind having to patch your window manager every time you want to extend it, go for [dwm](https://dwm.suckless.org). If you are a Haskell fan, try [xmonad](https://github.com/xmonad/xmonad). [There](https://github.com/Airblader/i3) [are](https://github.com/baskerville/bspwm) [several](http://www.qtile.org/) [options](https://awesomewm.org/) [to](http://www.herbstluftwm.org/) [choose](http://openbox.org/) [from](https://www.youtube.com/playlist?list=PL5--8gKSku17lbSBHPduj4qG97qxJe0UM). If you need a quick comparison between features of different WMs, check out [this](https://wiki.archlinux.org/index.php/Window_manager#List_of_window_managers) ArchWiki page or [this video](https://www.youtube.com/watch?v=Obzf9ppODJU) from DistroTube. Once you get the hang of how dotfiles work, check out the [unofficial guide to doing dotfiles](https://dotfiles.github.io/). Also, just bookmark [ArchWiki](https://wiki.archlinux.org) and [GentooWiki](https://wiki.gentoo.org/wiki/Main_Page), no matter which distro you are using.

_Updates: I have moved to `dwm` since December 2019._
]]></content:encoded>
        </item>
    </channel>
</rss>