Outlining and Bike

I’ve been planning to write this post for some time, but things kept coming up. Today, though, Jesse Grosjean is running a sale on Bike, his outlining app for the Mac, and it seemed like the right day to finally push this out. The sale is a pretty good one—$10 instead of the usual $30—and all you have to do to get that price is reply to this Mastodon toot with a photo of your hometown.

I’ve had a long an difficult relationship with outlining and outlining apps. I much prefer outlining to mind-mapping (so don’t write to me with mind-mapping recommendations), but I keep running into practical problems when using outlining apps. I categorize these problems as me problems and them problems.

The me problems have to do with converting an outline into a finished piece of writing. I’ve always had this silly belief that I should be able to convert an outline into the skeleton of a report (or a blog post or whatever, but it’s usually a report) more or less automatically and then flesh it out into a final product. This doesn’t work because, except for the items at the top-level, the various items and subitems in outlines don’t correspond perfectly to sections and subsections of a report. Some outline items are subsections, but most are paragraphs or lists within a subsection. There’s no general way of knowing what an outline item is; its level doesn’t offer enough information to slot it into the proper place in the report.

My solution to the me problem has been to stop trying to do the conversion automatically. I now write my reports from scratch starting with a blank text file while referring to my outline. The outline could be in a window on my Mac or open on my iPad propped up next to the Mac. If the outline happens to have paragraphs or lists that would fit nicely in the final report, I copy and paste them. Otherwise, I just type away, following the outline’s structure.

I confess this way of working still nags at me. Surely, the back of my brain says, there must be a way to avoid the repetition. But the front of my brain argues back that years of trying have never led to that magical solution. There’s no way to avoid the actual work of writing.

The them problems are about sharing my outlines with the people I’m working with. Quite often, when a report is in its early stages, sharing an outline with a colleague and going through its structure is a good way to organize and divvy up the work. But the people I collaborate with are seldom Mac users, and even if they were, they’re unlikely to have the same outlining software I have. When I was using OmniOutliner, I’d print my outline to a PDF document and share that. But getting my outline into a form I liked for review and sharing was never as easy as I thought it should be. I like my outlines to be very spare and unadorned as I’m working on them, but to have numbered sections and specific types of spacing when printed or displayed for review. OmniOutliner, being a WYSIWYG app, forced me to change my outline before printing to PDF and then change it back afterward. I suppose I could have automated a lot of this, but it just seemed wrong to have to do so.

In some ways, Bike seems worse than OmniOutliner for both the me and them problems. It’s Mac-only, so I can’t just open a Bike outline on my iPad to look at while I write. It doesn’t even have a Print menu item, so I can’t turn my outlines into PDFs for reviewing and sharing. But what it does have is a file format that makes it easy to get around these deficiencies.

A Bike outline in its native format is just an HTML file. Here’s a screenshot of a simple example:

Example Bike outline

And here’s the source of the Example.bike file:

<?xml version="1.0" encoding="UTF-8"?>
        <meta charset="utf-8"/>
        <ul id="le4V0ize">
            <li id="a0">
                <p>First item</p>
                    <li id="7R9">
                        <p>Subitem 1</p>
                    <li id="cCF">
                        <p>Subitem 2</p>
                            <li id="J8w">
                                <p>Subsubitem A</p>
                            <li id="A-L">
                                <p>Subsubitem B</p>
                    <li id="WxM">
                        <p>Subitem 3</p>
            <li id="iU">
                <p>Second item</p>
                    <li id="8C3">
                        <p>Subitem 1</p>
                    <li id="jIH">
                        <p>Subitem 2</p>
            <li id="aaD">
                <p>Third item</p>

As you can see, it’s basically just a bunch of nested unordered lists. You can open a Bike file in a browser, and it’s perfectly readable, albeit a bit on the vanilla side.

Bike outline opened in Safari

Since vanilla is not what I want, I wrote a short script to add a CSS section to the file that gives me the style I want.

Example outline in Safari after styling

Here’s the script, called bike2html:

 1:  #!/usr/bin/env python3
 3:  import sys
 4:  from docopt import docopt
 5:  import sys
 7:  usage = """Usage:
 8:    bike2html [options] BIKEFILE
10:  Convert a Bike outline to HTML with hierarchically numbered items.
12:  Options:
13:    -t TTTT   title [default: Outline]
14:    -h        show this help message
16:  """
18:  # Handle the command line option.
19:  args = docopt(usage)
20:  title = args['-t']
21:  bike = args['BIKEFILE']
23:  # CSS to insert after <head>
24:  css = '''    <style type="text/css">
25:        body {
26:          font-family: Helvetica, Arial, Sans-Serif;
27:          font-weight: normal;
28:          font-size: 12pt;
29:          line-height: 1.8em;
30:          margin: 0;
31:        }
32:        h1 {
33:          font-weight: bold;
34:          font-size: 20pt;
35:          line-height: 2em;
36:          text-align: center;
37:        }
38:        ul {
39:          list-style-type: none;
40:          counter-reset: item;
41:        }
42:        li {
43:          margin-top: .9em;
44:          counter-increment: item;
45:        }
46:        li::before {
47:          display: inline;
48:          content: counters(item, ".");
49:          padding-right: .75em;
50:        }
51:        li > p {
52:          display: inline;
53:        }
54:        @page {
55:          size: Letter;
56:          margin: 1in 1in .75in .5in;
57:        }
58:      </style>
59:  '''.splitlines(keepends=True)
61:  # Convert the input (first argument) to HTML with CSS
62:  with open(bike) as f:
63:    htmlLines = f.readlines()
65:  # Don't include the <?xml> line
66:  del htmlLines[:1]
68:  # Put the <style> section after <head> and the title after <body>
69:  headLine = htmlLines.index('  <head>\n')
70:  htmlLines[headLine+1:headLine+1] = css
71:  bodyLine = htmlLines.index('  <body>\n')
72:  htmlLines[bodyLine+1:bodyLine+1] = [f'    <h1>{title}</h1>\n']
74:  print(''.join(htmlLines), end='')

As you can see, much of the script is the CSS <style> section (Lines 24–59) that gets inserted into the file in Line 70. I use docopt to handle command-line options; currently, the only option is -t, which I use to set the title of the outline (with an <h1> tag) in Line 72. The script also deletes the <xml> line at the top of the original Bike file.

The only clever part of the script is the CSS that does the item numbering. That’s in Lines 38–50. I’m not sure where I learned how to do nested counters, but it was probably this Mozilla Developer Network page. You’ll note that even though Bike defines its outline items with <ul> tags, you can still assign numbers to them without changing them to <ol> tags.

Using bike2html is easy:

bike2html -t 'Example' Example.bike > Example.html

I suppose I should make the script smarter by using the filename as the default title.

I can send Example.html to anyone, and they’ll be able to open it. The nice thing about the “1.2.3” style of numbering the items is that it makes it easy for everyone who has the outline to refer to particular items on the phone or in an email.

You may be wondering how I can show Example.html on my iPad as I’m writing a report. Unlike Safari on the Mac, Safari on the iPad cannot open local files. There are two three ways to get around this:

  1. I can upload it to a server I control and open that file in Safari.
  2. I can open it in the Documents app from Readdle, which knows how to display HTML.
  3. I can start a local web server on my iPad via WorldWideWeb from The Iconfactory and view the page in Safari.

Update 11/25/2022 4:46 PM
Thanks to Andrew Kerr (on Mastodon!) for reminding me of WorldWideWeb. I bought WWW when it came out and used it for this very purpose a couple of months ago. Not sure why I stopped using it; it’s ideal for viewing this sort of static page.

Continuity allows me to select and copy text on the iPad and paste it on my Mac. It’s a nice way to work.

I should mention that I do enjoy outlining in Bike. It doesn’t have a huge number of features, but the features it has are what I need. I can see why other people might find it offputting for a writing app to not have a Print command, but it’s just right for me.

Hair splitting

A few days ago, Brady Haran at Numberphile released a new video with Ben Sparks. Like most of Ben’s videos, it’s interesting without being super heavy with math. In this case, I think the psychology behind the video is the most interesting part.

The puzzle is this:

What are the chances that there are two people in London with the same number of hairs on their head?

There’s a bit of misdirection in posing the problem in probability terms, as it might lead the listener to think he’s asked about the chances of two randomly selected people in London having the same number of hairs on their heads. A more straightforward—and therefore less tricky—question would be

Are there two people in London with the same number of hairs on their heads?

The answer comes from being able to estimate, within an order of magnitude or so, the number of people in London and the number of hairs on people’s heads. The former is a specific number that’s continually changing, so no one knows it except as a range. And the latter is a range by definition.

I think most people know that the population of London is at least several million. The tougher estimate is of the range of hairs on people’s heads. Ben suggests up to around 100,000, based on a hair density of 100 hairs per square centimeter over a 30 cm × 30 cm area. This hair density is equivalent to a hair spacing of about 1 mm, which seems reasonable to me.

(I’m no hair follicle expert, but any parent who’s gotten a message from their child’s school about instances of head lice showing up in class knows what it’s like to go through their kid’s scalp hair by hair.)

So with the range of hair counts at least an order of magnitude less than the population of London, there have to be at least two people with the same number of hairs. The “chances” asked for is 100%. Puzzle solved.

But Ben didn’t really ask for the solution itself. He wanted your gut reaction—before you did any hair-density calculations. And it’s probably not immediately obvious to most people that the number of hairs on someone’s is well under a million.

This is where the psychology comes in. While watching the video, I thought of a mathematically very similar question, but one that would, I believe, get instant correct answers from almost everyone:

What are the chances that there are two people in London who were born on the same day?

The number of possible birth dates of living people has to be around 40,000, which is within an order of magnitude of the hairs-on-head number. So the answer to this question is also 100%, but I bet most people would answer it correctly without hesitation and without calculation.

Even people who’ve never seen a birth notice in a newspaper probably know they exist. And they know that it’s common for there to be multiple notices every day in a big city. And those who don’t know about birth notices probably know that it’s common for large hospitals to have more than birth per day—and that big cities have many hospitals.

It’s the combination of the familiar—the hair on our heads—with the unfamiliar—how many hairs are there?—that makes Ben’s question interesting. My question, because it’s so easy, wouldn’t be interesting, even though it’s mathematically the same. Good thing Brady has Ben instead of me.

Announcing new posts on Mastodon

With lots of people, including me, either starting up new Mastodon accounts or dusting off the old ones they created during a previous Twitter crisis, I decided to write a script that would autotoot to Mastodon whenever I publish a new post here. It didn’t take long, as the Mastodon API is pretty easy to use. I put everything together while watching college football.1

If you want to write your own autotooting script, you’d do well to start by reading this post at DEV instead of following the Getting Started section of the API docs. The DEV post’s author, Joseph, makes the very useful suggestion to get your script’s authorization credentials through the Mastodon web interface instead of interacting with the API directly. Had I done that to begin with, it would have saved me about a quarter’s worth of time and frustration.

You get the authorization credentials by navigating to the Development page of your Mastodon account profile, clicking the NEW APPLICATION button, and filling in a couple of fields. Don’t bother changing the redirects or the scopes; the default entries will be fine.

User account Development page

With that done, you’ll have an access token to use in your script. The part of the script that creates a new status is really short, thanks to the wonders of Kenneth Reitz’s Requests module. Here are the key parts of my autotooting script:

 1:  #!/usr/bin/env python3
 3:  import requests
 5:  [ more imports ]
 7:  # Mastodon information.
 8:  murl = 'https://mastodon.cloud/api/v1/statuses'
 9:  auth = {'Authorization': 'Bearer XXXXXXXXXXXXXXXXXXXX'}
11:  [ Stuff to collect the info I want in the toot. ]
12:  [ The pieces of text I need are the summary ]
13:  [ and URL of the post. They're stored in variables ]
14:  [ named "summary" and "url." ]
16:  toot = {'status': f'''☃️ {summary}\n{url}'''}
18:  # Send the toot and return its URL.
19:  r = requests.post(murl, data=toot, headers=auth)
20:  print(r.json()['uri'])

The Mastodon URL you post to (Line 8) will depend on which server you’re hosted at. The bunch of Xs in Line 9 is replaced by the access token described above. The text of your toot goes into the status field of a dictionary (Line 16) that’s passed as the data parameter to the Requests post command (Line 19). That’s all.

Well, there is one other thing you may want to know. As you’re writing and debugging your script, you probably don’t want your followers to keep seeing your test toots. But you do want to see them yourself so you can make sure they’re working. To do that, add a visibility field to the toot dictionary.

16:  toot = {'status': f'''☃️ {summary}\n{url}''', 'visibility': 'direct'}

By setting the visibility to direct, it will act like a direct message to no one. Only you will see it. After debugging, just remove that part. Thanks to mdhughes for the tip.

  1. I will not mention the game, because the results were disgusting. I guess schools that are academically inferior need to win football games to make their alumni feel better about themselves. 

Are you a mod or a rocker?

This post was supposed to go up just a few days after the last one, but stuff got in the way. At least we’re in the same month.

Shortly after that post went up, I got an email from reader Jason Reene.

When I plug “tan^-1(cot(x))” into my TI-89 (still my go-to quick symbolic algebra tool) it returns “mod(-x, π) - π/2”

It took me a few minutes to convince myself that was an equivalent result.

It took me a lot longer to convince myself that it was equivalent, partly because I wasn’t sure how the TI-89 handles the mod function when the dividend is negative and the divisor is positive, but mostly because I was having a hard time figuring out which quadrant mod(-x, n) would be in for a given quadrant of x.

But it does work, even for angles well outside the (0, π/2) domain that my problem was restricted to. Here’s Mathematica’s plot of ArcTan[Cot[x]] over (-π, π):

ArcTan of Cot

And here’s its plot of Mod[-x, Pi] - Pi/2 over the same domain:

Mod minus half pi

Apparently, Mathematica treats the Mod function the same way Jason’s TI-89 does. Not all programming languages do.

After making these plots, I started thinking about the tangent, inverse tangent, and modulo functions, and how their definitions could easily change the answers Jason and I got.

Let’s start with tangent and its inverse. Here’s the tangent plotted over a decent range of angles:


To see its inverse, we exchange the horizontal and vertical axes:

Inverse tangent

Because this has multiple values for every argument, we have to choose which one our inverse tangent function will return. This is called the principal value. As far as I know, every programming language and every calculator chooses the one I’ve made a darker blue—the one that returns a value between -π/2 and π/2. So if you ask your calculator for the inverse tangent of a positive number, it gives you an angle in the first quadrant; if you ask it to give you the inverse tangent of a negative number, it gives you an angle in the fourth quadrant.

And if you’ve forgotten what that “quadrant” stuff is, this should refresh your memory. The arrows point in the positive directions.


Let’s move on to modulo. This is basically what you learned as “remainder” when you were first doing division. And when you’re dealing with positive integers only, modulo is exactly what you learned back then. Extending modulo to non-integer numbers is straightforward, but the tricky bits come when either the dividend (the number you’re dividing) or the divisor (the number you’re dividing it by) are negative.

For example, if you want -7 mod 3, you could think of it as

7 = 3 × (-2) – 1

so -7 mod 3 would be –1. Or you could think of it as

7 = 3 × (-3) + 2

and -7 mod 3 would be 2. Both answers are valid, but if you’re designing a programming language or a calculator, you have to choose one or the other. For this problem, Perl, Python, and Ruby return 2, while AppleScript and JavaScript return -1.

There’s a lot more to modulo—we haven’t discussed the divisor being negative—but that’s enough for our purposes. You can look up the various definitions on Wikipedia.

When the dividend is negative and the divisor positive, both Mathematica and the TI-89 return a positive result, which is why my Mod graph above (made in Mathematica) and the result from Jason’s calculator agree.

To show how tricky the TI-89’s formula is, let’s see how it transforms an angle in the first quadrant. We’ll use π/6, the same angle we converted with the ArcTan[Cot[θ]] formula in the last post. You can see the manipulations graphically in this image:

Angle manipulations for TI-89 formula

The hardest part to visualize is, of course, the modulo operation. It’s the smallest counterclockwise angle from an integer multiple of π to the purple line that was drawn in Step 2. In this case, the multiple of π we use is –π, the negative x-axis, and the CCW angle to the purple line is 5π/6.

As you can see, we do get to π/3, just not as easily as we did in the last post, where we just took the complement of the angle. On the other hand, this modulo formula works for angles outside of the first quadrant, and Jason’s TI-89 came up with it on its own. As you recall, Mathematica got stuck on ArcTan[Cot[θ]] and wouldn’t reduce it further. We had to figure out that it was the complement of π ourselves.

Thanks to Jason for an interesting view on this problem, and congratulations to the computer algebra people at Texas Instruments for a clever solution.