~bfiedler/website

81beacec76079e8148172758684e235d080b0801 — Ben Fiedler 1 year, 1 month ago
Initial commit
A  => .gitignore +2 -0
@@ 1,2 @@
public/
resources/_gen/

A  => assets/fira-code.css +43 -0
@@ 1,43 @@
@font-face{
    font-family: 'Fira Code';
    src: url('/fonts/eot/FiraCode-Light.eot');
    src: url('/fonts/eot/FiraCode-Light.eot') format('embedded-opentype'),
         url('/fonts/woff2/FiraCode-Light.woff2') format('woff2'),
         url('/fonts/woff/FiraCode-Light.woff') format('woff'),
         url('/fonts/ttf/FiraCode-Light.ttf') format('truetype');
    font-weight: 300;
    font-style: normal;
}

@font-face{
    font-family: 'Fira Code';
    src: url('/fonts/eot/FiraCode-Regular.eot');
    src: url('/fonts/eot/FiraCode-Regular.eot') format('embedded-opentype'),
         url('/fonts/woff2/FiraCode-Regular.woff2') format('woff2'),
         url('/fonts/woff/FiraCode-Regular.woff') format('woff'),
         url('/fonts/ttf/FiraCode-Regular.ttf') format('truetype');
    font-weight: 400;
    font-style: normal;
}

@font-face{
    font-family: 'Fira Code';
    src: url('/fonts/eot/FiraCode-Medium.eot');
    src: url('/fonts/eot/FiraCode-Medium.eot') format('embedded-opentype'),
         url('/fonts/woff2/FiraCode-Medium.woff2') format('woff2'),
         url('/fonts/woff/FiraCode-Medium.woff') format('woff'),
         url('/fonts/ttf/FiraCode-Medium.ttf') format('truetype');
    font-weight: 500;
    font-style: normal;
}

@font-face{
    font-family: 'Fira Code';
    src: url('/fonts/eot/FiraCode-Bold.eot');
    src: url('/fonts/eot/FiraCode-Bold.eot') format('embedded-opentype'),
         url('/fonts/woff2/FiraCode-Bold.woff2') format('woff2'),
         url('/fonts/woff/FiraCode-Bold.woff') format('woff'),
         url('/fonts/ttf/FiraCode-Bold.ttf') format('truetype');
    font-weight: 700;
    font-style: normal;
}

A  => assets/style.scss +305 -0
@@ 1,305 @@
$gray: #888888;
$fg: #000000;
$bg: #ffffff;
$link: #0000ee;

body {
    margin: 0;
    font-family: 'Fira Code', monospace;
}

.content {
    max-width: 920px;

    min-height: 100vh;
    min-height: -webkit-fill-available;

    display: grid;
    // see: https://css-tricks.com/preventing-a-grid-blowout/
    grid-template-columns: minmax(240px, 1fr);
    grid-template-rows: auto 1fr auto;
}

a {
    color: $fg;
}

#intro {
    display: flex;
    width: 100vw;
    min-height: 100vh;
    min-height: -webkit-fill-available;

    flex-direction: column;

    justify-content: center;
    align-items: center;
    text-align: center;

    h1 {
        font-size: 4rem;
        font-weight: normal;
    }

    ul {
        padding: 0;
        margin: 0 1em;

        li {
            display: inline-block;
        }

        li + li::before {
            content: " | "
        }
    }
}

.content > header {
    grid-column: 1;

    img {
        display: none;
    }

    > h1 {
        font-size: 1.8rem;
        font-weight: normal;
        margin: 10px 0;
        padding: 0;

        display: flex;
        justify-content: center;
        text-align: center;

        a {
            text-decoration: none;
        }
    }

    > nav {
        display: flex;
        justify-content: center;

        ul {
            padding: 0;
            margin: 0 1em;

            text-align: center;

            li {
                display: inline;
            }

            li + li::before {
                content: " | "
            }
        }

        dl {
            display: none;
        }
    }
}

.content main {
    grid-column: 1;

    margin: 0 0.5em;
    margin-top: 0.3rem;

    .list-title {
        display: none;
    }
}

.blogpost, .aboutme {
    header {
        margin: 0;
        padding: 0;

        .post-title {
            font-size: 1.2rem;
            margin-top: 0.5em;
        }
    }

    section { 
        h1, h2, h3, h4, h5, h6 {
            font-size: 1rem;
            margin: 1rem 0;
        }

        figure {
            display: flex;
            justify-content: center;
        }
    }

    pre, code {
        padding: 0 0.1rem;
        overflow-x: auto;
        color: #272822;
        background-color: #fafafa;
    }

    table {
        display: block;
        overflow-x: auto;
    }

    .footnotes {
        font-size: 0.8rem;

        ol {
            margin: 0.5rem 0;
        }

        p {
            margin: 0;
            margin-top: 0.5rem;
        }
    }
}

article {
    > header {
        h1 {
            margin: 0;
            margin-bottom: 2px;
        }

        > time {
            color: $gray;
            white-space: nowrap;
        }

        .post-metadata {
            color: $gray;

            a {
                color: $gray;
            }
        }
    }
}

.post-list {
    margin: 0;
    padding: 0;

    list-style-type: none;

    li {
        margin: 0;
        padding: 0;
    }

    article > header {
        margin: 0;
        display: flex;

        flex-direction: row;
        flex-wrap: nowrap;
        align-items: center;
    }

    h1 {
        font-size: 1rem;
        margin: 0.5rem;
    }
}

.content > footer {
    grid-row: 4 / 5;
    grid-column: 1 / 3;

    font-size: 0.9rem;
    margin-top: auto;
    //border-top: solid 2px;

    p {
        padding: 0;
        margin: 0.2rem 0.5em;
    }
}

@media (min-width: 768px) {
    .content {
        // see: https://css-tricks.com/preventing-a-grid-blowout/
        grid-template-columns: minmax(0, 1fr) 224px;
    }

    .content > header {
        grid-row: 1;
        grid-column: 1/3;

        img {
            margin: 0 auto;
            margin-top: 0.5rem;
            border-radius: 50%;
            display: block;
        }

        nav.detailed {
            margin: 10px 0;
            align-items: center;
            justify-content: left;

            > dl {
                display: block;
                font-size: 0.8rem;
                margin: 0;
                
                dd {
                    margin: 0;
                    margin-left: 1em;
                    margin-bottom: 5px;
                }
            }

            > ul {
                display: none;
            }
        }
    }

    header.detailed {
        grid-column: 2;

        h1 {
            font-size: 1.8rem;
            font-weight: normal;
            justify-content: center;
            margin: 0.5rem 0;

            a {
                text-decoration: none;
            }
        }

    }

    .content main {
        display: flex;
        flex-direction: column;

        grid-column: 1;
        grid-row: 1/3;

        .list-title {
            display: flex;
            justify-content: center;

            font-size: 1.6rem;
            font-weight: normal;

            margin: 0.5rem 0;
        }
    }

    main.blogpost {
        grid-column: 1/3;
        grid-row: 2;
    }
}

A  => assets/syntax.css +82 -0
@@ 1,82 @@
/* Background */ .chroma { color: #272822; background-color: #fafafa }
/* Other */ .chroma .x {  }
/* Error */ .chroma .err { color: #960050; background-color: #1e0010 }
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block; }
/* LineHighlight */ .chroma .hl { display: block; width: 100%;background-color: #ffffcc }
/* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
/* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
/* Keyword */ .chroma .k { color: #00a8c8 }
/* KeywordConstant */ .chroma .kc { color: #00a8c8 }
/* KeywordDeclaration */ .chroma .kd { color: #00a8c8 }
/* KeywordNamespace */ .chroma .kn { color: #f92672 }
/* KeywordPseudo */ .chroma .kp { color: #00a8c8 }
/* KeywordReserved */ .chroma .kr { color: #00a8c8 }
/* KeywordType */ .chroma .kt { color: #00a8c8 }
/* Name */ .chroma .n { color: #111111 }
/* NameAttribute */ .chroma .na { color: #75af00 }
/* NameBuiltin */ .chroma .nb { color: #111111 }
/* NameBuiltinPseudo */ .chroma .bp { color: #111111 }
/* NameClass */ .chroma .nc { color: #75af00 }
/* NameConstant */ .chroma .no { color: #00a8c8 }
/* NameDecorator */ .chroma .nd { color: #75af00 }
/* NameEntity */ .chroma .ni { color: #111111 }
/* NameException */ .chroma .ne { color: #75af00 }
/* NameFunction */ .chroma .nf { color: #75af00 }
/* NameFunctionMagic */ .chroma .fm { color: #111111 }
/* NameLabel */ .chroma .nl { color: #111111 }
/* NameNamespace */ .chroma .nn { color: #111111 }
/* NameOther */ .chroma .nx { color: #75af00 }
/* NameProperty */ .chroma .py { color: #111111 }
/* NameTag */ .chroma .nt { color: #f92672 }
/* NameVariable */ .chroma .nv { color: #111111 }
/* NameVariableClass */ .chroma .vc { color: #111111 }
/* NameVariableGlobal */ .chroma .vg { color: #111111 }
/* NameVariableInstance */ .chroma .vi { color: #111111 }
/* NameVariableMagic */ .chroma .vm { color: #111111 }
/* Literal */ .chroma .l { color: #ae81ff }
/* LiteralDate */ .chroma .ld { color: #d88200 }
/* LiteralString */ .chroma .s { color: #d88200 }
/* LiteralStringAffix */ .chroma .sa { color: #d88200 }
/* LiteralStringBacktick */ .chroma .sb { color: #d88200 }
/* LiteralStringChar */ .chroma .sc { color: #d88200 }
/* LiteralStringDelimiter */ .chroma .dl { color: #d88200 }
/* LiteralStringDoc */ .chroma .sd { color: #d88200 }
/* LiteralStringDouble */ .chroma .s2 { color: #d88200 }
/* LiteralStringEscape */ .chroma .se { color: #8045ff }
/* LiteralStringHeredoc */ .chroma .sh { color: #d88200 }
/* LiteralStringInterpol */ .chroma .si { color: #d88200 }
/* LiteralStringOther */ .chroma .sx { color: #d88200 }
/* LiteralStringRegex */ .chroma .sr { color: #d88200 }
/* LiteralStringSingle */ .chroma .s1 { color: #d88200 }
/* LiteralStringSymbol */ .chroma .ss { color: #d88200 }
/* LiteralNumber */ .chroma .m { color: #ae81ff }
/* LiteralNumberBin */ .chroma .mb { color: #ae81ff }
/* LiteralNumberFloat */ .chroma .mf { color: #ae81ff }
/* LiteralNumberHex */ .chroma .mh { color: #ae81ff }
/* LiteralNumberInteger */ .chroma .mi { color: #ae81ff }
/* LiteralNumberIntegerLong */ .chroma .il { color: #ae81ff }
/* LiteralNumberOct */ .chroma .mo { color: #ae81ff }
/* Operator */ .chroma .o { color: #f92672 }
/* OperatorWord */ .chroma .ow { color: #f92672 }
/* Punctuation */ .chroma .p { color: #111111 }
/* Comment */ .chroma .c { color: #75715e }
/* CommentHashbang */ .chroma .ch { color: #75715e }
/* CommentMultiline */ .chroma .cm { color: #75715e }
/* CommentSingle */ .chroma .c1 { color: #75715e }
/* CommentSpecial */ .chroma .cs { color: #75715e }
/* CommentPreproc */ .chroma .cp { color: #75715e }
/* CommentPreprocFile */ .chroma .cpf { color: #75715e }
/* Generic */ .chroma .g {  }
/* GenericDeleted */ .chroma .gd {  }
/* GenericEmph */ .chroma .ge { font-style: italic }
/* GenericError */ .chroma .gr {  }
/* GenericHeading */ .chroma .gh {  }
/* GenericInserted */ .chroma .gi {  }
/* GenericOutput */ .chroma .go {  }
/* GenericPrompt */ .chroma .gp {  }
/* GenericStrong */ .chroma .gs { font-weight: bold }
/* GenericSubheading */ .chroma .gu {  }
/* GenericTraceback */ .chroma .gt {  }
/* GenericUnderline */ .chroma .gl {  }
/* TextWhitespace */ .chroma .w {  }

A  => config.toml +44 -0
@@ 1,44 @@
baseURL = "http://tower.i.3fx.ch:1313"
languageCode = "en-us"
title = "Ben Fiedler"

pygmentsUseClasses = true
disableKinds = ["taxonomy", "term"]

[permalinks]
  blog = "/blog/:year/:month/:title"

[outputs]
  home = ["HTML"]
  blog = ["HTML", "RSS"]

[menu]
  [[menu.main]]
  identifier = "you are here"
  name = "Blog"
  url = "/blog/"
  weight = 1

  [[menu.main]]
  identifier = "Ben Fiedler"
  name = "About me"
  url = "/about/"
  weight = 2

  [[menu.main]]
  identifier = "bfiedler@mastodon.3fx.ch"
  name = "Mastodon"
  url = "https://mastodon.3fx.ch/@bfiedler"
  weight = 3

  [[menu.main]]
  identifier = "~bfiedler"
  name = "Sourcehut"
  url = "https://sr.ht/~bfiedler"
  weight = 4

  [[menu.main]]
  identifier = "ThreeFx"
  name = "GitHub"
  url = "https://github.com/ThreeFx"
  weight = 5

A  => content/about/index.md +35 -0
@@ 1,35 @@
---
title: "About me"
layout: "about"
---

I am a Master's student in [Computer Science](https://inf.ethz.ch) at [ETH
Zurich](https://ethz.ch). My focus is on Theoretical Computer Science and
Formal Methods. I also volunteer as one of the system administrators in
the [student association of computer science students](https://vis.ethz.ch).

## About this blog

A disclaimer in advance: I am not a frontend developer. Barring some basic HTML
I learned in school I have not written a single line of JavaScript and this blog
is my first encounter with HTML5/CSS. I have tried to make this website as
accessible as possible to screenreaders, if you have any suggestions how I can
improve on it, feel free to [open an
issue](https://github.com/ThreeFx/3fx-blog/issues).

I pride myself on the fact that this blog is free of JavaScript and obnoxious
trackers. All resources, including fonts, are served directly from the same
domain from which you are visiting this site, which means that no third-party
tracking is involved in any way.

This blog is statically generated by [Hugo](https://gohugo.io), served by
[nginx](https://nginx.com) and deployed using [Ansible](https://ansible.com). I
have written the entire styling myself in order to get more comfortable with
CSS. You can find the source of this blog hosted on
[GitHub](https://github.com), as well as the
[Ansible](https://github.com/ThreeFx/ansible-hugo)
[roles](https://github.com/ThreeFx/ansible-nginx) I use for
[deploying](https://github.com/ThreeFx/ansible-acme-sh) it.

All content is licensed under the [CC BY-SA
4.0](https://creativecommons.org/licenses/by-sa/4.0) license.

A  => content/blog/bash-graph.md +435 -0
@@ 1,435 @@
---
title: "Solving graph problems in bash"
date: 2019-11-22T18:30:40+01:00
---

Many interesting problems in computer science are expressible as problems on
graphs: Finding the shortest path between two vertices, calculating a spanning
tree, finding a large independent set, etc. Often in Theoretical Computer
Science we only look for a theoretical solution - once we know how to model a
specific problem as graph we give an algorithm in pseudocode and leave it at
that. We will try an unconventional method practically solving these problems
using bash.

<!--more-->

At ETH, Computer Science Master students learn how to efficiently (and
practically) solve graph problems in the Algorithms Lab, using
[C++](https://en.wikipedia.org/wiki/C%2B%2B) and
[BGL](https://www.boost.org/doc/libs/1_66_0/libs/graph/doc/index.html)[^6]. In
this course it is not enough to come up with a theoretical algorithm. One also
have to implement and pass testcases as part of his solution. The BGL offers
many sophisticated graph algorithms, each one cleverly optimized in order to
make the generated code as fast as possible. It does its job well: It makes it
(relatively) easy to use sophisticated graph theoretic algorithms in practice.
But it is hugo and complicated. Is there maybe an easier way to solve graph
problems on a computer, without having to implement most of the boring
algorithms and without installing a bazillion dependencies?

It turns out that we can (ab)use just about any Linux system for this, and we do
not even need a C++ compiler, or any other compiler for that matter. All we need
is any run-of-the-mill shell (bash will do just fine), and some standard system
utilities such as `find(1)`, `tr(1)`, `paste(1)`, `cut(1)`, etc.[^1], which (for
GNU/Linux systems) are packaged in the so-called [GNU
`coreutils`](https://www.gnu.org/software/coreutils/). Most other systems
provide similar, if not the same, commands - they may be packaged differently
however.

We will start with a simple problem: Given an undirected, acyclic and connected
graph, commonly known as _tree_, find the unique path between two vertices i and
j of said tree.

The first problem we have to solve is *representing* our graph. Recall from that
there are may possible ways to represent graphs: Adjacency lists, (for each
vertex list all its neighbors), Adjacency matrices (entry (i, j) is 1 iff edge
(i, j) exists), and many more. The most obvious solution would be to put all of
that information in a text file and then operate on that, but then we would be
no further than C++, since we would still have to implement most of the
algorithms by hand. So let us go up a step in the hierarchy: If files are bad,
can we maybe use directories instead?

It turns out that this works nicely, but only when our graph is a *rooted
tree*. In a rooted tree we choose an arbitrary vertex as root and then list all
vertices ordered by their distance from the root: First comes the root itself,
then the root's neighbors, then the root's neighbors' neighbors, etc. Let
us have a look at an example, a tree rooted at vertex 0:

{{< figure src="/blog/img/rooted-tree.svg" width="60%" >}}

We can easily translate this tree into a directory structure:

```bash
mkdir -p 0/1/3/{4,5}
mkdir -p 0/2/6
```

How do we display our tree? The best utility I know for this task is aptly named
`tree(1)`. Sadly, it is not part of the GNU coreutils and you most likely have
to install it with your favorite package manager. Running `tree` then shows us
the tree we constructed:

```
% tree
.
└── 0
    ├── 1
    │   └── 3
    │       ├── 4
    │       └── 5
    └── 2
        └── 6
```

Now we can answer questions such as "How can I get from 0 to i?", where i is an
arbitrary vertex other[^4] of our tree using the `find(1)` command. The `find`
command traverses all files in a given directory, and can optionally search for
specific criteria, for example directory names. If we wanted to know how to get
from vertex 0 to vertex 3 we can run the following command:

```
% find 0 -name 3
0/1/3
```

which tells us that we can go from 0 to 3 via vertex 1. In fact, we can
generalize this to arbitrary vertices i and j: First we check how to get from 0
to i, and then from 0 to j, and finally we have to find the common ancestor of
both paths. Then we reverse the path from 0 to i (now it is a path from i to 0),
walk along it until we hit the common ancestor of i and j, and then walk down
the path from the common ancestor to j.

The most difficult part of this procedure is finding the common ancestor. Lucky
for us the package `diffutils` provides the `cmp(1)` command, which can tell us
the first byte at which two files differ. The output of cmp is a bit verbose,
but using `cut(1)` and `tr(1)` from the coreutils we can extract the
relevant information:

{{< highlight bash >}}
% cmp <(echo "testa") <(echo "testb")
/proc/self/fd/11 /proc/self/fd/12 differ: byte 5, line 1
% cmp <(echo "testa") <(echo "testb") | cut -d " " -f 5 | tr -cd "[:digit:]"
5
{{< /highlight >}}

How does this work? The program `cut` takes a delimiter (`-d`) and some position
number (`-f`) and returns the string at the given position (1-indexed) when
splitting on the delimiter specified by `-d`. We use `tr` to delete (`-d`) all
non-digits (`-c` stands for the complement of the character set) to ensure that
we get `5` as result, and not `5,`. So far so good, let us look where we are
now:

{{< highlight bash >}}
# In Bash arguments are passed implicitly and referenced
# using $1, $2, etc. We use $1 as i and $2 as j
path_between() {
    path_to_i=$(find -name $1)
    path_to_j=$(find -name $2)

    first_diff_byte=$(cmp <(echo $path_to_i) <(echo $path_to_j) \
            | cut -d " " -f 5 \
            | tr -c -d '[:digit:]')
{{< /highlight >}}

It remains to find the common ancestor of i and j. Since we know where the paths
differ, we know up to which character they are identical. Using [bash variable
expansion](https://www.tldp.org/LDP/abs/html/parameter-substitution.html) we can
identify the common prefix, and using `cut` extract the vertex they have in
common. The `rev` command from the `util-linux` package reverses a string of
characters:

{{< highlight bash >}}
    common_prefix=${path_to_i:0:(( $first_diff_byte - 1 ))}
    common_ancestor=$(echo -n $common_prefix | rev | cut -d / -f 2 | rev)
{{< /highlight >}}

The part in the double parenthesis is subject to the `expr(1)` command[^2], which
and evaluates the expression as arithmetic expression. Now all we have left to
do is to stick all of it together:

{{< highlight bash >}}
    # if we omit local here we set $PATH...
    local path=$(echo ${path_to_i#"$common_prefix"} \
        | tr '/' $'\n' \
        | tac \
        | paste -s -d '/')
    path=$path/$common_ancestor/${path_to_j#"$common_prefix"}

    echo $path
}
{{< /highlight >}}

Using the `tr`, `tac` and `paste` commands (all part of coreutils) we can
reverse the path from i to the common ancestor of i and j. We do this by
changing the slashes which delimit our vertices to newlines and using
`tac`[^3] to reverse the order of our vertices. Finally
we can use `paste` to put the reversed vertices back together in a single string
and then append the path from the common ancestor to j.

Testing it on our input graph gives the following output:

```
% path_between 4 5
4/3/5
% path_between 5 4
5/3/4
% path_between 0 5
/.//1/3/5
% path_between 3 4
/1//4
```

It seems like it fails when the common ancestor is either i or j. We can remedy
thisby handling these cases separately: If i is the common ancestor, we can
simply take the path from i to j minus the path from 0 to the ancestor of i.
Else if j is the common ancestor then we have to first reverse the path from j
to i (so we get the path from i to j) and the remove the path to j's ancestor.

{{< highlight bash >}}
if [[ $common_prefix == $path_to_i ]]; then
    local path=$1${path_to_j#"$common_prefix"}
elif [[ $common_prefix == $path_to_j ]]; then
    local path=$(echo ${path_to_i#"$common_prefix"} \
        | tr '/' $'\n' \
        | tac \
        | paste -s -d /)
    path=$path$2
else
    # Do what we did before
fi
{{< /highlight >}}

Now it gives correct results for any query:

```
% path_between 3 4
3/4
% path_between 4 3
4/3
% path_between 6 4
6/2/0/1/3/4
% path_between 0 1
0/1
% path_between 1 0
1/0
```

That's it. In 20 lines of bash we wrote a pathfinding algorithm for trees. Not
only that, but we did it without needing to compile any code, only using tools
available on most Linux systems! I find that quite impressive.

### Pathfinding in general graphs

Restricting ourselves to rooted trees sounds pretty limiting doesn't it? Can we
generalize our model to arbitrary graphs? At first it seems like we cannot,
since then our file system (which is where we store our graph) does not support
cycles. After all, no directory can be a (grand-)parent of itself. But it turns
out that using [symbolic links](https://en.wikipedia.org/wiki/Symbolic_link) we
can "point" to other directories, which can in turn point to other directories,
which can point to other directories, etc. We can create symlinks using the `ln`
command (incidentally also found in coreutils), by writing `ln -s <directory
point to> <name of the link>`. For each vertex we want to create a directory and
in that directory create symbolic links to all of its neighbors.  If you recall
the different forms of graph representation before this corresponds to adjacency
lists. Let us try this with a simple example:

{{< figure src="/blog/img/graph.svg" width="60%" >}}

We can create the graph as follows:

```
% mkdir `seq 0 5`
% ln -s ../0 1
% ln -s ../1 0
% ln -s ../2 0
% ln -s ../0 2
% <continue for each edge>
% tree
.
├── 0
│   ├── 1 -> ../1
│   └── 2 -> ../2
├── 1
│   ├── 0 -> ../0
│   └── 2 -> ../2
├── 2
│   ├── 0 -> ../0
│   ├── 1 -> ../1
│   └── 4 -> ../4
├── 3
│   └── 4 -> ../4
├── 4
│   ├── 2 -> ../2
│   ├── 3 -> ../3
│   └── 5 -> ../5
└── 5   
    └── 4 -> ../4
```

Now let us look at the same problem as before: We want to specify two vertices i
and j, and want to find a path between them (if it exists).  Last time we used
the `find(1)` utility to find these. This time we will explicitly tell `find` in
which vertex i we would like to start by specifying it as the root of our
search:

```
% find 0/ -name 1
0/1
% find 0/ -name 3
<nothing>
```

Hmm, it seems as if `find` does not follow the symbolic links. Looking at
`find`'s manual page (`man find`) we see the following

```
OPTIONS The  -H, -L and -P options control the treatment of symbolic links.

[...]

-P     Never  follow  symbolic  links.  This is the default behaviour.  When
       find examines or prints information a file, and the file  is  a  sym‐
       bolic  link,  the information used shall be taken from the properties
       of the symbolic link itself.

-L     Follow symbolic links.  When  find  examines  or  prints  information
       about  files, the information used shall be taken from the properties
       of the file to which the link points, not from the link  itself  (un‐
       less  it  is  a broken symbolic link or find is unable to examine the
       file to which the link points).  Use of this option implies  -noleaf.
       If  you later use the -P option, -noleaf will still be in effect.  If
       -L is in effect and find discovers a symbolic link to a  subdirectory
       during  its  search, the subdirectory pointed to by the symbolic link
       will be searched.
```

Aha, we have to specify `find -L` if we want to follow symlinks. But our
symlinks induce cycles in the file system, what happens if `find` encounters a
cycle? Again the manpage has the answer for us:

```
The POSIX standard requires that find detects loops:

       The find utility shall detect infinite loops;  that  is,  entering  a
       previously visited directory that is an ancestor of the last file en‐
       countered.  When it detects an infinite loop, find shall write a  di‐
       agnostic message to standard error and shall either recover its posi‐
       tion in the hierarchy or terminate.
```

So we are good to go!

```
% find -L 0/ -name 3
find: File system loop detected; ‘0/2/4/5/4’ is part of the same file system loop as ‘0/2/4’.
0/2/4/3
find: File system loop detected; ‘0/2/4/3/4’ is part of the same file system loop as ‘0/2/4’.
find: File system loop detected; ‘0/2/4/2’ is part of the same file system loop as ‘0/2’.
find: File system loop detected; ‘0/2/1/2’ is part of the same file system loop as ‘0/2’.
find: File system loop detected; ‘0/2/1/0’ is part of the same file system loop as ‘0/’.
find: File system loop detected; ‘0/2/0’ is part of the same file system loop as ‘0/’.
find: File system loop detected; ‘0/1/2/4/5/4’ is part of the same file system loop as ‘0/1/2/4’.
0/1/2/4/3
find: File system loop detected; ‘0/1/2/4/3/4’ is part of the same file system loop as ‘0/1/2/4’.
find: File system loop detected; ‘0/1/2/4/2’ is part of the same file system loop as ‘0/1/2’.
find: File system loop detected; ‘0/1/2/1’ is part of the same file system loop as ‘0/1’.
find: File system loop detected; ‘0/1/2/0’ is part of the same file system loop as ‘0/’.
find: File system loop detected; ‘0/1/0’ is part of the same file system loop as ‘0/’.
```

We get two paths and a whole lot of errors that find detected a loop. We can
just ignore these:

```
% find 0/ -name 3 2>/dev/null
0/2/4/3
0/1/2/4/3
```

Perfect! Now the find command returns all the paths between 0 and 3 in the
graph. And it is much simpler than our previous solution: `find` does all the
work for us.

This approach can easily be extended to directed graphs by only symlinking in
the direction the edge goes, which means that we can find all paths between
vertices i and j in any graph in *a single line of bash*. That's pretty amazing,
don't you think? We can even improve the solution a bit: What if we only want *a*
path, and not all paths? The manual page of `find` once again has the answer:

```
-quit  Exit immediately.  No child processes will be left  running,  but  no
       more  paths specified on the command line will be processed.  For ex‐
       ample, find /tmp/foo /tmp/bar -print -quit will print only  /tmp/foo.
       Any  command  lines  which  have been built up with -execdir ... {} +
       will be invoked before find exits.  The exit status may or may not be
       zero, depending on whether an error has already occurred.
[...]
-print True; print the full file name on the standard output, followed by  a
       newline.
```

If we specify `-print` and `-quit` the `find` command will immediately exit when
it finds the first path:

```
% find -L 0/ -name 3 -print -quit 2>/dev/null
0/2/4/3
```

This works on any graph, directed or undirected, and will print a path if it
finds one. Since `find` will never continue running if it found a loop this is
successful even if no paths exist between the given vertices:

```
% find -L 0/ -name 6 -print -quit 2>/dev/null
<nothing>
```

Note that `find` operates by [depth first
search](https://en.wikipedia.org/wiki/Depth-first_search), and thus might not
find the shortest path between two vertices. The last thing we will show is how
to (ab)use the `mindepth` and `maxdepth` options in order to force find to do a
[breadth first search](https://en.wikipedia.org/wiki/Breadth-first_search),
which always finds the shortest path in unweighted graphs:

{{< highlight bash >}}
find_shortest() {
    local i=0
    local p=""
    while [[ -z $p ]]; do
        p=$(find -L $1 -mindepth $i -maxdepth $i -name $2 -print -quit 2>/dev/null)
        i=$(( i+1 ))
    done
    echo $p
}

# Output
% find_shortest 0 3
0/2/4/3
{{< /highlight >}}

While we have not found a path, we tell find to extend its search radius by one,
and once we found our desired path we output it. This approach is guaranteed to
always return the shortest path, although it is computationally more expensive
since we often traverse the same parts, and it also won't detect if no path
exists between these vertices. We could extend this program to add termination
detection, however then it would lose its elegance, which is what I wanted to
show in this post.

### Closing remarks

Interestingly enough, somebody [actually implemented
`bfs(1)`](https://github.com/tavianator/bfs), which behaves like `find` and
internally uses breadth-first search, but it is not (and probably will never) be
part of coreutils.

I did use this algorithm in one of my courses to solve small pathfinding
problems[^5]. Implementing this was quite fun
and not as much pain as I'd feared. Sometimes these `clever' solutions have their
merit - although for more sophicated problems I'd rather stick to C++ and BGL.

[^1]: The numbers in parenthesis correspond to the man page section of the programs. Most programs have their manpage in section 1, which is for user commands, If you are interested in more details or the other sections `man man` gives great insight.
[^2]: Also a part of the coreutils package
[^3]: Like `cat` but reverses the order of the lines
[^4]: Actually the case where we input the same number twice is tedious since our solution depends on the paths being different, as you will see later.
[^5]: and so far I haven't been kicked out
[^6]: short for Boost Graph Library

A  => content/blog/changeme-base64.md +51 -0
@@ 1,51 @@
---
title: "'changeme' is valid base64"
date: 2019-12-09T00:10:58+01:00
tags: base64, short-and-sweet
---

[Base64-encoding](https://en.wikipedia.org/wiki/Base64) is ubiquitous in our
modern world. Many programs communicate in base64-encoded messages, since these
have nice properties: They consist of a very limited subset of ASCII characters,
and can thus be displayed as text. E-Mails for example are commonly encoded in
base64 in transfer, which can be seen in the `Content-Transfer-Encoding` header.

Where I volunteer we have a tradition of putting `changeme` as a placeholder for
many template variables indicating that they should be overwritten later. Thus,
if we later still see `changeme` anywhere we know that somewhere somebody must
have forgotten to specify a variable[^1].

Sometimes these template variables should correspond to base64-encoded content,
i.e. the base64-decoding of said variable should produce meaningful content. But
what happens if somebody forgets to define said variable, and it is substituted
with `changeme` instead? I always thought that it would fail to
decode the string since the probability that `changeme` is actually valid base64
encoding must be very low. But lo and behold, this is not the case:

```
% echo changeme | base64 -d
r�[1m
```

Running `hexdump` on the output we can identify the characters:

```
% echo changeme | base64 -d | hexdump -C
00000000  72 16 a7 81 e9 9e      |r.....|
00000006
```

Mostly nonsense, but the second byte is interesting: It is an ASCII control
character [^2], meaning trips up all sorts of programs which expect to read
printable ASCII characters, such as JSON deserializers.

This bug actually occurred when I was migrating some applications between
different Kubernetes clusters: In the old cluster the application ran without
problems, but I forgot to specify some variable in the new cluster, and it lead
to Go complaining about non-ASCII characters in a JSON string.

I guess the moral of the story is: If you use dummy values be sure that they
cannot be misinterpreted.

[^1]: this is not the ideal way to solve this, but we have our historical reasons for choosing this solution.
[^2]: specifically `Ctrl+V`

A  => content/blog/img/graph.svg +95 -0
@@ 1,95 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
 "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.43.20190912.0211 (20190912.0211)
 -->
<!-- Title: %3 Pages: 1 -->
<svg width="320pt" height="188pt"
 viewBox="0.00 0.00 320.00 188.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 184)">
<title>%3</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-184 316,-184 316,4 -4,4"/>
<!-- 0 -->
<g id="node1" class="node">
<title>0</title>
<ellipse fill="none" stroke="black" cx="64" cy="-162" rx="18" ry="18"/>
<text text-anchor="middle" x="64" y="-158.3" font-family="Times,serif" font-size="14.00">0</text>
</g>
<!-- 10 -->
<!-- 4 -->
<g id="node6" class="node">
<title>4</title>
<ellipse fill="none" stroke="black" cx="202" cy="-90" rx="18" ry="18"/>
<text text-anchor="middle" x="202" y="-86.3" font-family="Times,serif" font-size="14.00">4</text>
</g>
<!-- 10&#45;&#45;4 -->
<!-- 9 -->
<!-- 10&#45;&#45;9 -->
<!-- 3 -->
<g id="node3" class="node">
<title>3</title>
<ellipse fill="none" stroke="black" cx="248" cy="-162" rx="18" ry="18"/>
<text text-anchor="middle" x="248" y="-158.3" font-family="Times,serif" font-size="14.00">3</text>
</g>
<!-- 8 -->
<!-- 3&#45;&#45;8 -->
<!-- 5 -->
<g id="node10" class="node">
<title>5</title>
<ellipse fill="none" stroke="black" cx="248" cy="-18" rx="18" ry="18"/>
<text text-anchor="middle" x="248" y="-14.3" font-family="Times,serif" font-size="14.00">5</text>
</g>
<!-- 3&#45;&#45;5 -->
<!-- 6 -->
<!-- 6&#45;&#45;0 -->
<!-- 2 -->
<g id="node5" class="node">
<title>2</title>
<ellipse fill="none" stroke="black" cx="110" cy="-90" rx="18" ry="18"/>
<text text-anchor="middle" x="110" y="-86.3" font-family="Times,serif" font-size="14.00">2</text>
</g>
<!-- 6&#45;&#45;2 -->
<!-- 1 -->
<g id="node8" class="node">
<title>1</title>
<ellipse fill="none" stroke="black" cx="64" cy="-18" rx="18" ry="18"/>
<text text-anchor="middle" x="64" y="-14.3" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- 6&#45;&#45;1 -->
<!-- 2&#45;&#45;0 -->
<g id="edge10" class="edge">
<title>2&#45;&#45;0</title>
<path fill="none" stroke="black" d="M100.48,-105.49C92.59,-117.49 81.43,-134.48 73.53,-146.5"/>
</g>
<!-- 2&#45;&#45;10 -->
<!-- 2&#45;&#45;4 -->
<g id="edge11" class="edge">
<title>2&#45;&#45;4</title>
<path fill="none" stroke="black" d="M128.33,-90C146.84,-90 165.36,-90 183.87,-90"/>
</g>
<!-- 2&#45;&#45;1 -->
<g id="edge9" class="edge">
<title>2&#45;&#45;1</title>
<path fill="none" stroke="black" d="M100.47,-74.5C92.57,-62.48 81.41,-45.49 73.52,-33.49"/>
</g>
<!-- 2&#45;&#45;9 -->
<!-- 4&#45;&#45;3 -->
<g id="edge13" class="edge">
<title>4&#45;&#45;3</title>
<path fill="none" stroke="black" d="M211.52,-105.49C219.41,-117.49 230.57,-134.48 238.47,-146.5"/>
</g>
<!-- 4&#45;&#45;8 -->
<!-- 4&#45;&#45;5 -->
<g id="edge14" class="edge">
<title>4&#45;&#45;5</title>
<path fill="none" stroke="black" d="M211.53,-74.5C219.43,-62.48 230.59,-45.49 238.48,-33.49"/>
</g>
<!-- 8&#45;&#45;5 -->
<!-- 1&#45;&#45;0 -->
<g id="edge12" class="edge">
<title>1&#45;&#45;0</title>
<path fill="none" stroke="black" d="M64,-36.19C64,-63.52 64,-116.58 64,-143.87"/>
</g>
<!-- 9&#45;&#45;4 -->
</g>
</svg>

A  => content/blog/img/rooted-tree.svg +85 -0
@@ 1,85 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
 "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.43.0 (0)
 -->
<!-- Title: t Pages: 1 -->
<svg width="260pt" height="125pt"
 viewBox="0.00 0.00 260.00 125.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 121)">
<title>t</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-121 256,-121 256,4 -4,4"/>
<!-- 0 -->
<g id="node1" class="node">
<title>0</title>
<ellipse fill="none" stroke="black" cx="18" cy="-45" rx="18" ry="18"/>
<text text-anchor="middle" x="18" y="-41.3" font-family="Times,serif" font-size="14.00">0</text>
</g>
<!-- 1 -->
<g id="node2" class="node">
<title>1</title>
<ellipse fill="none" stroke="black" cx="90" cy="-72" rx="18" ry="18"/>
<text text-anchor="middle" x="90" y="-68.3" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- 0&#45;&#45;1 -->
<g id="edge1" class="edge">
<title>0&#45;&#45;1</title>
<path fill="none" stroke="black" d="M35.24,-51.26C46.54,-55.62 61.56,-61.42 72.85,-65.77"/>
</g>
<!-- 2 -->
<g id="node3" class="node">
<title>2</title>
<ellipse fill="none" stroke="black" cx="90" cy="-18" rx="18" ry="18"/>
<text text-anchor="middle" x="90" y="-14.3" font-family="Times,serif" font-size="14.00">2</text>
</g>
<!-- 0&#45;&#45;2 -->
<g id="edge2" class="edge">
<title>0&#45;&#45;2</title>
<path fill="none" stroke="black" d="M35.24,-38.74C46.54,-34.38 61.56,-28.58 72.85,-24.23"/>
</g>
<!-- 3 -->
<g id="node4" class="node">
<title>3</title>
<ellipse fill="none" stroke="black" cx="162" cy="-72" rx="18" ry="18"/>
<text text-anchor="middle" x="162" y="-68.3" font-family="Times,serif" font-size="14.00">3</text>
</g>
<!-- 1&#45;&#45;3 -->
<g id="edge3" class="edge">
<title>1&#45;&#45;3</title>
<path fill="none" stroke="black" d="M108.3,-72C119.15,-72 133.08,-72 143.9,-72"/>
</g>
<!-- 6 -->
<g id="node7" class="node">
<title>6</title>
<ellipse fill="none" stroke="black" cx="162" cy="-18" rx="18" ry="18"/>
<text text-anchor="middle" x="162" y="-14.3" font-family="Times,serif" font-size="14.00">6</text>
</g>
<!-- 2&#45;&#45;6 -->
<g id="edge6" class="edge">
<title>2&#45;&#45;6</title>
<path fill="none" stroke="black" d="M108.3,-18C119.15,-18 133.08,-18 143.9,-18"/>
</g>
<!-- 4 -->
<g id="node5" class="node">
<title>4</title>
<ellipse fill="none" stroke="black" cx="234" cy="-99" rx="18" ry="18"/>
<text text-anchor="middle" x="234" y="-95.3" font-family="Times,serif" font-size="14.00">4</text>
</g>
<!-- 3&#45;&#45;4 -->
<g id="edge4" class="edge">
<title>3&#45;&#45;4</title>
<path fill="none" stroke="black" d="M179.24,-78.26C190.54,-82.62 205.56,-88.42 216.85,-92.77"/>
</g>
<!-- 5 -->
<g id="node6" class="node">
<title>5</title>
<ellipse fill="none" stroke="black" cx="234" cy="-45" rx="18" ry="18"/>
<text text-anchor="middle" x="234" y="-41.3" font-family="Times,serif" font-size="14.00">5</text>
</g>
<!-- 3&#45;&#45;5 -->
<g id="edge5" class="edge">
<title>3&#45;&#45;5</title>
<path fill="none" stroke="black" d="M179.24,-65.74C190.54,-61.38 205.56,-55.58 216.85,-51.23"/>
</g>
</g>
</svg>

A  => content/blog/long-monotone-trails.md +47 -0
@@ 1,47 @@
---
title: "Long monotone trails"
date: 2019-12-10T10:10:10+01:00
tags: graph-theory, short-and-sweet
---

During a recent dinner discussion I was presented with a beautiful proof to the
following problem: Given an arbitrary graph <math><mi>G</mi> <mo>=</mo> (<mi>V</mi>, <mi>E</mi>)</math>, and an
ordering <math><mi>ϕ</mi></math> of <math><mi>E</mi></math>, what is the longest monotone trail
in <math><mi>G</mi></math> that is guaranteed to exist?

<!--more-->

A trail in a graph is a sequence of edges such that

 * adjacent edges share an incident vertex and
 * no edge is traversed twice

Take a few moments to think about this problem before reading the solution, as
it such a simple seeming problem is quite tricky: Clearly there are graphs where
there exists no trail of length greater than zero, namely the empty graph. Thus
the solution must somehow correlate with some graph property.

### The solution

It turns out that the longest monotone trail has length equal to the average
degree <math><mover><mi>d</mi> &#x203E;</mover></math> of <math><mi>G</mi></math>. The proof is truly
astouding:

Put a person on each vertex of <math><mi>G</mi></math>, and for each edge
<math><mi>e</mi></math> in <math><mi>E</mi></math>, traversed in order of
<math><mi>ϕ</mi></math>, exchange the two people standing at vertices incident
to <math><mi>e</mi></math>. Each person will walk a monotone trail on
<math><mi>G</mi></math>.

All people together traversed a distance of <math><mi>2</mi> <mo>\*</mo> |E|</math> units,
and since there are <math>|<mi>V</mi>|</math> people the average distance
traveled is <math><mi>2</mi> <mo>\*</mo> |<mi>E</mi>| / |<mi>V</mi>|</math>, which is
exactly the average degree of <math><mi>G</mi></math>. By the [pigeonhole
principle](https://en.wikipedia.org/wiki/Pigeonhole_principle) at least one
person must have traveled <math><mi>2</mi> <mo>\*</mo> |<mi>E</mi>| / |<mi>V</mi>|</math>
units, which concludes our proof.

It is not difficult to see that this result is best possible. If
<math><mi>G</mi></math> is a matching on <math><mi>2</mi> <mi>n</mi></math>
vertices, then the average degree in <math><mi>G</mi></math> is <math>1</math>,
and the longest monotone trail also has length <math>1</math>.

A  => content/blog/math.pdc +18 -0
@@ 1,18 @@
---
title: "Pandoc testing: lalalalal"
date: 2019-12-10T10:10:10+01:00
tags: pandoc, test
draft: true
---

This is a test

## test title

# test title

$a + b \leq c$

$$
\phi^2 + \psi_2^5 - 15 = \log_2 e^10
$$

A  => content/blog/test.md +147 -0
@@ 1,147 @@
+++
author = "Hugo Authors"
title = "Markdown Syntax Guide"
date = "2019-03-11"
description = "Sample post showcasing basic Markdown syntax and formatting for HTML elements."
tags = [
    "markdown",
    "css",
    "html",
    "themes",
]
categories = [
    "themes",
    "syntax",
]
series = ["Themes Guide"]
aliases = ["migrate-from-jekyl"]
draft = true
+++

This post offers a sample of basic Markdown syntax that can be used in Hugo content files, also it shows whether basic HTML elements are decorated with CSS in a Hugo theme.
<!--more-->

## Headings

The following HTML `<h1>`—`<h6>` elements represent six levels of section headings. `<h1>` is the highest section level while `<h6>` is the lowest.

# H1
## H2
### H3
#### H4
##### H5
###### H6

## Paragraph

Xerum, quo qui aut unt expliquam qui dolut labo. Aque venitatiusda cum, voluptionse latur sitiae dolessi aut parist aut dollo enim qui voluptate ma dolestendit peritin re plis aut quas inctum laceat est volestemque commosa as cus endigna tectur, offic to cor sequas etum rerum idem sintibus eiur? Quianimin porecus evelectur, cum que nis nust voloribus ratem aut omnimi, sitatur? Quiatem. Nam, omnis sum am facea corem alique molestrunt et eos evelece arcillit ut aut eos eos nus, sin conecerem erum fuga. Ri oditatquam, ad quibus unda veliamenimin cusam et facea ipsamus es exerum sitate dolores editium rerore eost, temped molorro ratiae volorro te reribus dolorer sperchicium faceata tiustia prat.

Itatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sapicia is sinveli squiatum, core et que aut hariosam ex eat.

## Blockquotes

The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a `footer` or `cite` element, and optionally with in-line changes such as annotations and abbreviations.

#### Blockquote without attribution

> Tiam, ad mint andaepu dandae nostion secatur sequo quae.
> **Note** that you can use *Markdown syntax* within a blockquote.

#### Blockquote with attribution

> Don't communicate by sharing memory, share memory by communicating.</p>
> — <cite>Rob Pike[^1]</cite>


[^1]: The above quote is excerpted from Rob Pike's [talk](https://www.youtube.com/watch?v=PAAkCSZUG1c) during Gopherfest, November 18, 2015.

## Tables

Tables aren't part of the core Markdown spec, but Hugo supports supports them out-of-the-box.

   Name | Age
--------|------
    Bob | 27
  Alice | 23

#### Inline Markdown within tables

| Inline&nbsp;&nbsp;&nbsp;     | Markdown&nbsp;&nbsp;&nbsp;  | In&nbsp;&nbsp;&nbsp;                | Table      |
| ---------- | --------- | ----------------- | ---------- |
| *italics*  | **bold**  | ~~strikethrough~~&nbsp;&nbsp;&nbsp; | `code`     |

## Code Blocks

#### Code block with backticks

```
html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Example HTML5 Document</title>
</head>
<body>
  <p>Test</p>
</body>
</html>
```
#### Code block indented with four spaces

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Example HTML5 Document</title>
    </head>
    <body>
      <p>Test</p>
    </body>
    </html>

#### Code block with Hugo's internal highlight shortcode
{{< highlight html >}}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Example HTML5 Document</title>
</head>
<body>
  <p>Test</p>
</body>
</html>
{{< /highlight >}}

## List Types

#### Ordered List

1. First item
2. Second item
3. Third item

#### Unordered List

* List item
* Another item
* And another item

#### Nested list

* Item
1. First Sub-item
2. Second Sub-item

## Other Elements — abbr, sub, sup, kbd, mark

<abbr title="Graphics Interchange Format">GIF</abbr> is a bitmap image format.

H<sub>2</sub>O

X<sup>n</sup> + Y<sup>n</sup> = Z<sup>n</sup>

Press <kbd><kbd>CTRL</kbd>+<kbd>ALT</kbd>+<kbd>Delete</kbd></kbd> to end the session.

Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures.

A  => layouts/_default/about.html +9 -0
@@ 1,9 @@
{{ define "main" }}
{{ partial "header.html" (dict "context" . "IsDetailed" false "DisplayImage" true) }}
<main class="blogpost">
    <article class="aboutme">
	<section>{{ .Content }}</section>
    </article>
</main>
{{ partial "footer.html" }}
{{ end }} 

A  => layouts/_default/baseof.html +19 -0
@@ 1,19 @@
<!DOCTYPE html>
<html lang="{{ .Site.LanguageCode }}">
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>{{ block "title" . }}{{ .Title }}{{ end }}</title>
        {{- $css := resources.Get "style.scss" | resources.ToCSS | resources.Fingerprint }}
        <link rel="stylesheet" href="{{ $css.RelPermalink }}" integrity="{{ $css.Data.Fingerprint }}">
        {{- $font := resources.Get "fira-code.css" | resources.Fingerprint }}
        <link rel="stylesheet" href="{{ $font.RelPermalink }}" integrity="{{ $font.Data.Fingerprint }}">
        {{- $syntax := resources.Get "syntax.css" | resources.Fingerprint }}
        <link rel="stylesheet" href="{{ $syntax.RelPermalink }}" integrity="{{ $syntax.Data.Fingerprint }}">
        {{- with .OutputFormats.Get "rss" -}}
        {{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }}
        {{- end -}}
    </head>
    <body{{ if not .IsHome }} class="content"{{ end }}>
        {{- block "main" . }}{{ end }}
    </body>
<html>

A  => layouts/_default/home.html +12 -0
@@ 1,12 @@
{{ define "main" }}
<main id="intro">
    <h1>Ben Fiedler</h1>
    <nav>
        <ul>
            {{- range .Site.Menus.main }}
            <li><a href="{{ .URL }}">{{ .Name }}</a></li>
            {{- end }}
        </ul>
    </nav>
</main>
{{ end }}

A  => layouts/blog/list.html +20 -0
@@ 1,20 @@
{{ define "title" }}Blog posts{{ end }}
{{ define "main" }}
{{ partial "header.html" (dict "context" . "IsDetailed" true "DisplayImage" true) }}
<main>
    <h1 class="list-title">{{ block "title" . }}{{ end }}</h1>
    <ol class="post-list">
        {{- range .Pages.ByPublishDate.Reverse }}
        <li>
            <article>
                <header>
                    <time datetime="{{ .Date.Format "2006-01-02T15:04:05Z0700" }}">{{ .Date.Format "2006-01-02" }}</time>
                    <h1><a href="{{ .RelPermalink }}">{{ .Title }}</a></h1>
                </header>
            </article>
        </li>
        {{- end }}
    </ol>
</main>
{{ partial "footer.html" }}
{{ end }}

A  => layouts/blog/rss.xml +39 -0
@@ 1,39 @@
{{- $pctx := . -}}
{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}}
{{- $pages := slice -}}
{{- if or $.IsHome $.IsSection -}}
{{- $pages = $pctx.RegularPages -}}
{{- else -}}
{{- $pages = $pctx.Pages -}}
{{- end -}}
{{- $limit := .Site.Config.Services.RSS.Limit -}}
{{- if ge $limit 1 -}}
{{- $pages = $pages | first $limit -}}
{{- end -}}
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>3fx.ch</title>
    <link>https://3fx.ch/blog</link>
    <description>Recent posts on Ben's blog</description>
    <generator>Hugo -- gohugo.io</generator>{{ with .Site.LanguageCode }}
    <language>{{.}}</language>{{end}}{{ with .Site.Author.email }}
    <managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Author.email }}
    <webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
    <copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
    <lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
    {{- with .OutputFormats.Get "RSS" -}}
    {{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }}
    {{- end -}}
    {{ range $pages }}
    <item>
      <title>{{ .Title }}</title>
      <link>{{ .Permalink }}</link>
      <pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
      {{ with .Site.Author.email }}<author>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</author>{{end}}
      <guid>{{ .Permalink }}</guid>
      <description>{{- .Content | html -}}</description>
    </item>
    {{ end }}
  </channel>
</rss>

A  => layouts/blog/single.html +13 -0
@@ 1,13 @@
{{ define "main" }}
{{ partial "header.html" (dict "context" .  "IsDetailed" false) }}
<main class="blogpost">
    <article>
        <header>
            <h1 class="post-title">{{ .Title }}</h1>
            <span class="post-metadata"><time datetime="{{ .Date.Format "2006-01-02T15:04:05Z0700" }}">{{ .Date.Format "2006-01-02" }}</time> on <a href="/blog/">this blog</a></span>
        </header>
	<section>{{ .Content }}</section>
    </article>
</main>
{{ partial "footer.html" . }}
{{ end }} 

A  => layouts/partials/footer.html +4 -0
@@ 1,4 @@
<footer>
    <p>&copy; {{ now.Year }} Ben Fiedler.
    <a href="https://creativecommons.org/licenses/by-sa/4.0/legalcode">Some rights reserved.</a>
</footer>

A  => layouts/partials/header.html +19 -0
@@ 1,19 @@
<header{{ if default false .IsDetailed }} class="detailed"{{ end }}>
    {{- if default false .DisplayImage }}
    <img src="/img/me/96px.jpg">
    {{- end }}
    <h1><a href="{{ .context.Site.BaseURL }}">Ben Fiedler</a></h1>
    <nav{{ if default false .IsDetailed }} class="detailed"{{ end }}>
        <dl>
            {{- range .context.Site.Menus.main }}
            <dt>{{ .Name | lower }}</dt>
            <dd><a href="{{ .URL }}">{{ .Identifier }}</a></dd>
            {{- end }}
        </dl>
        <ul>
            {{- range .context.Site.Menus.main }}
            <li><a href="{{ .URL }}">{{ .Name }}</a></li>
            {{- end }}
        </ul>
    </nav>
</header>

A  => static/fonts/eot/FiraCode-Bold.eot +0 -0
A  => static/fonts/eot/FiraCode-Light.eot +0 -0
A  => static/fonts/eot/FiraCode-Medium.eot +0 -0
A  => static/fonts/eot/FiraCode-Regular.eot +0 -0
A  => static/fonts/ttf/FiraCode-Bold.ttf +0 -0
A  => static/fonts/ttf/FiraCode-Light.ttf +0 -0
A  => static/fonts/ttf/FiraCode-Medium.ttf +0 -0
A  => static/fonts/ttf/FiraCode-Regular.ttf +0 -0
A  => static/fonts/woff/FiraCode-Bold.woff +0 -0
A  => static/fonts/woff/FiraCode-Light.woff +0 -0
A  => static/fonts/woff/FiraCode-Medium.woff +0 -0
A  => static/fonts/woff/FiraCode-Regular.woff +0 -0
A  => static/fonts/woff2/FiraCode-Bold.woff2 +0 -0
A  => static/fonts/woff2/FiraCode-Light.woff2 +0 -0
A  => static/fonts/woff2/FiraCode-Medium.woff2 +0 -0
A  => static/fonts/woff2/FiraCode-Regular.woff2 +0 -0
A  => static/img/me/96px.jpg +0 -0