Computational science approach to programming

Sometimes, I wondered why people write code in a certain way, usually inefficient, lengthy or just plain weird. There’s a classic

int i;
char c[3];
i = 0;
c[i++] = 'a';
c[i++] = 'b';
c[i++] = 'c';

when

char c[3];
c[0] = 'a';
c[1] = 'b';
c[2] = 'c';

works just as well, and more readable. Perhaps they thought, “Hey there’s an array! Must iterate something!”. They didn’t give any thought to why the array was used in the first place.

And so I had a theory. What if they were so engrossed in the intricacies of the programming language, that they’ve lost sight of the problem they were trying to solve?

Which brings me to computational science. I gave a short explanation at the start of another article, but it didn’t do justice to it. You might want to check out the differences between computational science and computer science as described by Wikipedia.

I majored in computational science and applied mathematics. What happened was that I’d be given a problem (usually scientific or math based), and I’d have to solve it. Calculations by hand on paper were done. After the theory of the solution was understood, and practical use (with physically calculations) were done, it’s time to scale up by coding the solution method.

For example, Gaussian elimination on matrices. It’s used to solve a system of linear equations, where the coefficients in the equations were used to form the matrices. Coefficients are just the number in front of the (math) variable. So in the equation
2x + 5y = 7
the coefficient of x is 2.

The theory was taught and calculations were done by hand on small matrices. I worked on 2 by 3, 3 by 4 and even 4 by 5 matrices. One small slip in entering figures into the calculator, and I’d have to start all over again.

Then I had to implement the method in code. To do that, I needed to understand the theory and the solution behind the method. I did calculations by hand, so I knew the pain. And then I wrote the code. The problem given was to solve a 100 by 101 matrix. It’d probably take a whole night to do by hand. Assuming I don’t punch the wrong numbers on the calculator or read the wrong figures. The computer probably took only a few seconds.

And I believe that is what’s really behind computational science. My studies taught me to solve problems first. If the solution benefit from the calculation speed of computers and ease of repetitiveness with computers (which is usually the case), then it’s go ahead and write the code to implement the solution.

A programmer could agonise over the implementation of some feature, without thinking if that feature could be solved in another way, an easier-to-implement way.

For example, the programmer could be frustrated by the tedium of retrieving values from the database, adding them up with intermediate variables in code, and then storing them back into the database. It’s very painful to do that in C (I know, I’ve seen it). Did it ever occur to the programmer that he could just do the summation and store the sum entirely within the database environment?

All he needed was to write code to execute a SQL statement or stored procedure. Which is much simpler than keeping track of temporary variables, worrying about floating point errors and hold database records in memory for looping.

And that’s how I write code, the computational science approach to programming. Think about the problem, solve the actual problem, then use the computer’s capabilities to scale the solution.

You just need to know enough to maintain code

When I was in university, every homework program was written fresh. There was no maintenance; the code was as new as definitions allow. For every problem, I read through the math or science (or common sense in some cases) required, thought up the logic, and proceeded to write the code.

Since copying was generally frowned upon, when helping fellow students, I had to come up with explanations that they understood, sometimes even coming up with new code.

When I first joined the workforce as a professional, all that changed. I was introduced to numerous programs and scripts, each with hundreds of lines of code. Each program was filled with business logic that was partly stored in documentation and partly stored in the heads of my colleagues and manager.

And I didn’t write a single line of that code.

The programs and scripts were run on a monthly basis, and generated lots of output reports. So the first order of things was to learn the broad idea of the whole system. That was over in, I don’t know, a few days?

Next came supporting the users. I had to look at reports and make sure the relevant numbers tally. I was dealing with financial data, so if there was a difference of even 0.01 cent, I needed to know and correct the error before the users look at the reports (there’s still enough time buffer).

The programs dealt with massive amounts of data. I’m talking millions of database records. Sometimes, something fails. I get to be the one being paged (yes, the pager was still around then) by the operators on shift duty. A few Saturdays and Sundays were spent at the office looking at data files, scrolling through lines of code and doing update statements in the database for correction.

As each month went by, my familiarity with the programs and business logic grew. I reached a point where I no longer needed to be hand-held by my then mentor colleague. Occasionally, I still ask for help, but I was trusted enough to maintain the code myself. Which was fortunate, because there was a new project then, and my manager and colleague were full into the meetings and request evaluations.

There were support calls to answer and user requests to fulfil. The new project required new programs to be written, and involved changes to existing programs. It was at this point that I learnt something due to task and time management.

I didn’t need to know everything about my existing programs to make useful changes.

The new project, new business rules, new code to be written, and support calls from users and operators, forced me to be effective in identifying lines of code for change, for maintenance or for correction. I didn’t have to be familiar with every program, every line of code, every nuance of business rules to do maintenance work. I just needed to know enough.

That required that I understand the code as was understood by the original coder. To the point where I not only know where to look, and also where not to look.

You must understand that when companies hire you, they want you to become productive as soon as possible. Spending months understanding framework code and business logic while you basically did nothing useful is a waste of resources. It’s not full understanding and then start being useful. It’s incremental steps to understanding and small increases to productivity, back and forth, in a cycle.

So what’s the takeaway lesson for you? Don’t get bogged down in the immensity of the code you’re facing and maintaining.

Floating point errors – Sine X Taylor series

Stop rolling your eyes. I actually have a point to make, and the Taylor series (from math) is the best candidate to illustrate it. Think of it as expanding your education.

In simple terms, the Taylor series is a polynomial approximating another function. For example, the Taylor series for the trigonometry function sin(x) is
x – x^3/3! + x^5/5! – x^7/7! …
(x^3 means x to the power of 3, or x*x*x. 5! means 5 factorial, or 1*2*3*4*5)

As you can see, the series is infinite. You’ll also note that if you leave only the first term, sin(x) = x. Remember my warning about the pitfall.

So, that’s about all the math knowledge you need. That was easy, right? Our problem is in calculating that series. Suppose you don’t have a math library in your code base, then you’ll need to resort to the Taylor series to help approximate the sine function.

In an ideal world, the math logic you implement in code will evaluate exactly in value. Because of the finiteness of representing numbers in computers, you will get an approximate answer. And it’s more “approximaty” if you come up with the wrong algorithm for implementation too.

Note that factorial values can grow very big. 12! is 479001600, which is the limit for a 32-bit integer (13! is 6227020800, exceeding 2147483648 = 2^31). Suppose we want to stick to 32-bit integer variables, then this means the Taylor series for sin(x) can only go up to
x – x^3/3! + x^5/5! – x^7/7! + x^9/9! – x^11/11!

6 terms only. It doesn’t feel very accurate… What if we could “remove” the factorial? Note that
x^5/5! = x/1 * x/2 * x/3 * x/4 * x/5
Then we could have more terms when we reduce the factorials into floating point values.

And I’m going to let you in on another secret. The direction in which you add the terms together yields different results! So your calculation for
x – x^3/3! + x^5/5! – x^7/7! + x^9/9! – x^11/11!
gives a slightly different answer for
- x^11/11! + x^9/9! – x^7/7! + x^5/5! – x^3/3! + x

This happens because of the limitation of the variable type you use to store intermediate results. Generally speaking, you should start with the smallest intermediate values and work your way up. So this:
- x^11/11! + x^9/9! – x^7/7! + x^5/5! – x^3/3! + x
is preferred. The factorial in the denominator increases faster than the exponential in the numerator, so higher order terms are smaller in value.

In a following code sample, I’ve labelled sections A, B, C, D, E, and F.

Sections A, C and E contain code where the for loops are in ascending order. Sections B, D and F contain code where the for loops are in descending order.

Sections A and B form the pair where the float variable type is used to store intermediate values.

Sections C and D form the pair where the double variable type is used.

Sections E and F still use the double variable type, and the implementation logic for x^n/n! is do x^n and n! separately, then divide x^n by n!. This reduces the number of floating point operations (by about half actually), and so reduces the calculation errors.

Because of the use of factorial, we’ll use a long (a 64-bit integer in C# and appropriate operating system) which has more capacity to store the intermediate factorial value.

We’ll be calculating sin(PI), so the Taylor series is
PI – PI^3/3! + PI^5/5! – …

Alright, a short lesson in math. Odd numbers can be represented in the form of (2n + 1), where n is an integer. Hence this part of the code

k = 2 * i + 1;

And this part does the alternating minus and plus sign of the series

if (i % 2 == 1) fBuffer = -fBuffer;

And here’s the code:

const int cnTerms = 9;
float floatx = (float)Math.PI;
double doublex = Math.PI;
float fAsc, fDesc, fBuffer;
double fAscInDouble, fDescInDouble, fBufferInDouble;
double fAscAlt, fDescAlt;
long iBuffer;
int i, j, k;
StreamWriter sw = new StreamWriter("taylorsinepi.txt");

fAsc = 0.0f;
for (i = 0; i < cnTerms; ++i)
{
	k = 2 * i + 1;
	fBuffer = 1.0f;
	for (j = 0; j < k; ++j)
	{
		fBuffer *= floatx / (float)(j + 1);
	}
	if (i % 2 == 1) fBuffer = -fBuffer;
	fAsc += fBuffer;
	sw.WriteLine("A {0,0:d2} {1,0:f8}", i, fBuffer);
}
sw.WriteLine();

fDesc = 0.0f;
for (i = cnTerms - 1; i >= 0; ----i)
{
	k = 2 * i + 1;
	fBuffer = 1.0f;
	for (j = k - 1; j >= 0; ----j)
	{
		fBuffer *= floatx / (float)(j + 1);
	}
	if (i % 2 == 1) fBuffer = -fBuffer;
	fDesc += fBuffer;
	sw.WriteLine("B {0,0:d2} {1,0:f8}", i, fBuffer);
}
sw.WriteLine();

fAscInDouble = 0.0;
for (i = 0; i < cnTerms; ++i)
{
	k = 2 * i + 1;
	fBufferInDouble = 1.0;
	for (j = 0; j < k; ++j)
	{
		fBufferInDouble *= doublex / (double)(j + 1);
	}
	if (i % 2 == 1) fBufferInDouble = -fBufferInDouble;
	fAscInDouble += fBufferInDouble;
	sw.WriteLine("C {0,0:d2} {1,0:f16}", i, fBufferInDouble);
}
sw.WriteLine();

fDescInDouble = 0.0;
for (i = cnTerms - 1; i >= 0; ----i)
{
	k = 2 * i + 1;
	fBufferInDouble = 1.0;
	for (j = k - 1; j >= 0; ----j)
	{
		fBufferInDouble *= doublex / (double)(j + 1);
	}
	if (i % 2 == 1) fBufferInDouble = -fBufferInDouble;
	fDescInDouble += fBufferInDouble;
	sw.WriteLine("D {0,0:d2} {1,0:f16}", i, fBufferInDouble);
}
sw.WriteLine();

fAscAlt = 0.0;
for (i = 0; i < cnTerms; ++i)
{
	k = 2 * i + 1;
	fBufferInDouble = 1.0;
	iBuffer = 1;
	for (j = 0; j < k; ++j)
	{
		fBufferInDouble *= doublex;
		iBuffer *= (j + 1);
	}
	if (i % 2 == 1) fBufferInDouble = -fBufferInDouble;
	fAscAlt += (fBufferInDouble / (double)iBuffer);
	sw.WriteLine("E {0,0:d2} {1,0:f16}", i, fBufferInDouble);
}
sw.WriteLine();

fDescAlt = 0.0;
for (i = cnTerms - 1; i >= 0; ----i)
{
	k = 2 * i + 1;
	fBufferInDouble = 1.0;
	iBuffer = 1;
	for (j = k - 1; j >= 0; ----j)
	{
		fBufferInDouble *= doublex;
		iBuffer *= (j + 1);
	}
	if (i % 2 == 1) fBufferInDouble = -fBufferInDouble;
	fDescAlt += (fBufferInDouble / (double)iBuffer);
	sw.WriteLine("F {0,0:d2} {1,0:f16}", i, fBufferInDouble);
}
sw.WriteLine();

sw.WriteLine("ASC     : {0}", fAsc.ToString("f16"));
sw.WriteLine("DESC    : {0}", fDesc.ToString("f16"));
sw.WriteLine("ASC   x2: {0}", fAscInDouble.ToString("f16"));
sw.WriteLine("DESC  x2: {0}", fDescInDouble.ToString("f16"));
sw.WriteLine("ASC  ALT: {0}", fAscAlt.ToString("f16"));
sw.WriteLine("DESC ALT: {0}", fDescAlt.ToString("f16"));

sw.Close();

And this is the output:

A 00 3.14159300
A 01 -5.16771300
A 02 2.55016400
A 03 -0.59926460
A 04 0.08214591
A 05 -0.00737043
A 06 0.00046630
A 07 -0.00002192
A 08 0.00000080

B 08 0.00000080
B 07 -0.00002192
B 06 0.00046630
B 05 -0.00737043
B 04 0.08214591
B 03 -0.59926460
B 02 2.55016400
B 01 -5.16771300
B 00 3.14159300

C 00 3.1415926535897900
C 01 -5.1677127800499700
C 02 2.5501640398773500
C 03 -0.5992645293207920
C 04 0.0821458866111282
C 05 -0.0073704309457144
C 06 0.0004663028057676
C 07 -0.0000219153534478
C 08 0.0000007952054001

D 08 0.0000007952054001
D 07 -0.0000219153534478
D 06 0.0004663028057676
D 05 -0.0073704309457144
D 04 0.0821458866111282
D 03 -0.5992645293207920
D 02 2.5501640398773400
D 01 -5.1677127800499700
D 00 3.1415926535897900

E 00 3.1415926535897900
E 01 -31.0062766802998000
E 02 306.0196847852810000
E 03 -3020.2932277767900000
E 04 29809.0993334462000000
E 05 -294204.0179738900000000
E 06 2903677.2706132800000000
E 07 -28658145.9693880000000000
E 08 282844563.5865330000000000

F 08 282844563.5865330000000000
F 07 -28658145.9693880000000000
F 06 2903677.2706132800000000
F 05 -294204.0179738900000000
F 04 29809.0993334462000000
F 03 -3020.2932277767900000
F 02 306.0196847852810000
F 01 -31.0062766802998000
F 00 3.1415926535897900

ASC     : -0.0000000102118100
DESC    : 0.0000000000000000
ASC   x2: 0.0000000224195107
DESC  x2: 0.0000000224195102
ASC  ALT: 0.0000000224195103
DESC ALT: 0.0000000224195102

I’ve printed the intermediate values for better comparison. And it looks beautiful that the section pairs appear symmetrical. So let’s see the first pair of results.
-0.0000000102118100 versus 0.0000000000000000

That don’t look very equal to me. I don’t know why we got zero for the second one (maybe the alternating sign code is wrong). I mean the correct answer is zero, just that it shouldn’t be from an approximation algorithm…

Anyway, the next pair looks promising:
0.0000000224195107 versus 0.0000000224195102
Except the last digit.

The last pair has different last digits too.

So what have we learnt? Use the correct variable type. And come up with a good algorithm. The most accurate variable type can’t save you if your algorithm is faulty. Change the cnTerms constant to something else, like 8 (which gives a more believable disparity for the first pair), to get a feel of the algorithm, code and results.

For your information, sin(PI) is zero, which is the value the algorithms are iteratively approaching.

A dragonfly skimming water surface

Concerned I am.

From my own adventures in this thing people call blogging, certain advice given I was.

  • Write compelling headlines (because people can’t be bothered to read anything more)
  • Use lists (because people can’t be bothered to read anything more)
  • Use images to break up text (because people can’t be bothered to read anything more)

I’ve read of students multitasking, of students surfing the Internet, doing homework for other classes, uploading pictures to Facebook, and doing shopping at eBay, all during lecture time. Sure the professor may really be boring, but still, have they done anything at all? Were they that pressed for time?

Imagination

There was a time in the past when informercials just came out, and I saw this speed reading package and thought, “How cool is that!”. After some deliberation, I bought it. I read through the instructions, I listened to the audio, and some time thereafter, decided that being able to boast to my friends that I could read at cheetah speeds was not worth it.

I’m a visual person. When I read, I conjure up images, scenes and even sounds to accompany the words. The product promised that I could still read at lower speeds for leisure, but that’s not the point. Even for deep reading and thinking stuff, such as study material, I need that imagery. It’s how I learn and recall. Sprinting through words rob me of that.

That student situation was supposedly a normal occurrence in America. People talk of information overload, and not just in America. There seems to be pressure to keep up with what’s going on. In the world, on the streets, about your friends. There’s the feeling of being left out if you’re not connected to the Internet somehow. This video of the Twitter Whore (her words, not mine) exemplifies this extreme case.

Concerned I am. Are people losing the ability to slow down, ponder and think deep?

Don’t need to think

I came across this article by Nicholas Carr: Is Google Making Us Stupid? He talked of the Internet and search engines combining to provide instant gratification. Instant answers, quick news bites, skim, click, easily distracted, skim, click.

The search engine had given rise to a problem in programming forums. People couldn’t find the code to their problem with search engines, so they turned to programming forums. The thing was, they asked their questions in exactly the same way. They posted their question and expected an answer with no effort on their part.

Code answers can only be copied and pasted up to a certain extent. Sooner or later, you’ll find yourself in a situation where you have to come up with something unique. On your own.

Yes, you can piece something together from different parts (which is part of what this site’s about). But you have to come up with that idea that pieces together separate code solutions into a solution for your problem. That idea has to come from you.

The Internet is a vast repository of information. Search engines provide an easy way to sift through that and come up with something, hopefully the answer. But the information comes from people, not computers. People who’ve thought, researched, analysed and then published that information.

Being able to “plug in” to the Internet’s wealth of knowledge can be empowering, akin to maybe the hive mind of bees. Perhaps some individuals find their own lives minuscule, and possibly unbearable. And only when they’re awash in the presence of, and illusionary omniscience of the Internet, do they find peace.

Which reminds me of a scene in the latest Indiana Jones movie, the one with the crystal skull. That agent of the Soviet Union, Irina Spalko, wanted to know everything. Her wish was granted. I find the part where she was begging “please, no more!” just a little amusing. Knowledge has to be acquired through one’s efforts (thinking, experimenting), and not just given.

Carr also said it’s unsettling about Google’s

easy assumption that we’d all “be better off” if our brains were supplemented, or even replaced, by an artificial intelligence

People complain about their jobs being outsourced to other countries, but they don’t find anything wrong with the idea of their thinking being outsourced?

Words and imagery

I’ve also read about print advertisements, and how they still work better than television advertisements. Print ads, such as those in newspapers and magazines, consist primarily of words. Print ads, surrounded by the context in which they’re read, require readers to come up with images, sounds and feelings to accompany those words. In contrast, television ads already came up with the imagery for the viewer.

Of course, print ads require more effort to capture the attention of the reader, but the effect is more lasting, because the reader comes up with his own version of how the product can make his life better. Television ads require little imagination from the viewer, less engagement from the viewer, and hence less attention. In the words of Carr,

In the quiet spaces opened up by the sustained, undistracted reading of a book, or by any other act of contemplation, for that matter, we make our own associations, draw our own inferences and analogies, foster our own ideas.

The dragonfly

The Chinese have this phrase, “qing ting dian shui“. It literally translates to “dragonfly touch water”. The full description should probably be “dragonfly skimming over water surface and lightly touching it (from time to time)”. Sometimes, I’m amazed at how Chinese characters squeeze so much meaning into them.

It’s usually in reference to a martial artist, being so skilful in his art, that he can lighten his body to the point where he can run on water, gently touching the surface on each step. Like a dragonfly.

Are people becoming dragonflies? Do they think skimming material, lightly grabbing news bites, flitting from hyperlink to hyperlink, of being hyper-connected, will help them in coping with information overload, of being in the information age? Are we losing the ability to do deep reading and thinking, to contemplate and reflect?

Concerned I am. Because dragonflies, in their adult stage, live only up to about 4 months.

Conversation with a blog

Blog: You’re staring at me again.
Me: What?
Blog: You’ve been staring at me for 15 minutes now. Got the writer’s block, huh?
Me: I’m thinking, ok? … What’s it to you anyway?
Blog: It’s just painful watching you type a few words, stare, type a bit, delete, and stare again.
Me: Well, I’ve got the main points. I just need to fill in the details.
Blog: …
Me: That’s not really why you’re talking to me, are you? So what’s up?

Blog: It’s just, well …
Me: Yes?
Blog: I, *shift position*, uh…
Me: Uh huh?
Blog: … uh *suddenly perks up* what about Saving Private Ryan?
Me: What?
Blog: You know, that guy you stuck in the stone ruins?
Me: *raise eye brow*
Blog: He solved some puzzle, and then you got him to go into the underground cave?
Me: Oh, right. I was going to save him. I mean I am going to save him. And he’s not a private. He’s not even in the military.
Blog: So what is he?
Me: Ryan’s a programmer, and also very good at solving puzzles. And I’m revealing too much already. Hey, are you taking down notes? *rising tone of alarm*
Blog: Uh huh.
Me: Hey delete that! You can’t publish that. All the mystery would be out.
Blog: Your readers will want to know what’s going to happen.
Me: Yes, of course. I have the story all mapped out. I just need to come up with an appropriate puzzle.
Blog: Riiight…
Me: Hey, it’s hard coming up with something unique, ok? I want to introduce a concept, it has to be moderately hard to figure out, and it has to fit into the story. It’s just hard ok?

Blog: I see you’re also slacking off. *checks files* You’re not posting as much.
Me: I’m re-prioritising. There’s a difference.
Blog: Semantics.
Me: Well, it’s just … my readers don’t seem to be talking to me much. They don’t seem to want to talk about what I wrote too. I don’t know if what I wrote was complete brilliance or utter rubbish. Either one could be the reason for their inertia.
Blog: Why don’t you just ask them?
Me: I did! At least I thought I did. A few people replied, probably out of pity *voice cracks, small voice* Can’t believe I’m talking to some nosy writing software…
Blog: Hey! I resent that.
Me: *smiles* I’m sorry. You’re a good friend. Oh yeah, where’s my contact page?
Blog: *points at a spot*
Me: Right. See? People can ask me any questions with that, or tell me I’m right on this or wrong on that or …

Blog: …
Me: Something’s still on your mind. Alright, cough it up. What’s going on?
Blog: It’s just, I’ve worked very hard for you.
Me: Yes, I know. And I thank you for it.
Blog: I mean, I displayed *checks some files* 197 posts for you.
Me: Wow! Has it been that many?
Blog: And got my friend, Defensio, to keep out spam. *checks another file* Like 2820 of them.
Me: Oh my goodness!
Blog: I work day and night …
Me: I work nights too …
Blog: *sniffs* 24 hours a day …
Me: I appreciate that …
Blog: 7 days a week. *sniffs* I even changed my entire outfit for you!
Me: Hey …
Blog: And what have you done for me? *voice cracks*
Me: Wait, are you crying?

Blog: Do you even know what day today is?
Me: Uh, it’s a Thursday?
Blog: *flips through folder, and grabs a sheet of paper* Here!
Me: *takes paper and read* … Oh, that’s my first post! Did I really write that? Oh man, that’s so embarrassing…
Blog: *bawls* Look at the date!
Me: Ok, ok… it’s 12 June, year 2007. What’s so speci… oh. Today’s 12 June, isn’t it?
Blog: *bawls some more*
Me: Hey hey hey… *grabs tissue paper and hand it over* Please stop crying.
Blog: *sniffs, wipe tears*
Me: I’m sorry, ok? You’re one year old today, aren’t you?

Blog: *nods*
Me: Happy birthday! I’m sorry, I totally forgot about it…
Blog: *tightens lips, eyes tearing*
Me: Hey, hey, hear me out! I’m sorry I didn’t get you a present.
Blog: *wipes tears*
Me: I have some plans, and one of them involves making you popular.
Blog: *in small voice* Really?
Me: Alright, maybe not celebrity popular, but more well-known. I’m also thinking of …
Blog: Not talking about math formulas?
Me: Getting some sponsorship … what? Hey, math is what makes you stand out, you know?
Blog: It does?
Me: It’s not like I write about differentiation of math equations… well, at least not yet.
Blog: Well, write more about programming stuff.
Me: I can’t exactly go around answering non-existent programming questions, can I? I’m looking…
Blog: Well, look harder. *sniff* I’m starting to feel a little lonely.
Me: It takes time and effort. I’m trying really hard…

Blog: There’s a programming party going on at Alltop, and I just barely got invited. It sucks to not know anybody.
Me: Hey, tone down your language.
Blog: You use the word “suck”.
Me: Well, I’m different. I’m a grown up.
Blog: Not by very much it seems. You still get mistaken for a student.
Me: Well, I have boyishly good looks… wait a minute, are you still taking notes?
Blog: Uh huh.
Me: Don’t you dare publish that! … Oh dear, it’s already published, isn’t it?
Blog: I’m a blog. That’s what I do.
Me: *groans*
Blog: Relax. Think of it as a birthday present for me.
Me: Right. *sighs*
Blog: And remember that popular thing.
Me: Of course. Now go bother someone else on the Internet, I’ve got some writing to do. And to find a hole to hide…