<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4222267598459829544</id><updated>2012-01-19T03:10:38.191-08:00</updated><category term='thinkpad'/><category term='android'/><category term='tricks'/><category term='sysadmin'/><category term='git'/><category term='web'/><category term='books'/><category term='maths'/><category term='programming'/><category term='puzzles'/><category term='shamelessplug'/><category term='design'/><category term='games'/><category term='music'/><title type='text'>Ben Lynn: Openly Geeky</title><subtitle type='html'>Games, puzzles, coding, maths, etc.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>90</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-7442732949739965367</id><published>2011-04-07T21:12:00.000-07:00</published><updated>2011-08-05T02:15:58.537-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>List Comprehensions in C</title><content type='html'>&lt;div id="preamble"&gt; &lt;div class="sectionbody"&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Some introductions to Haskell monads begin with the IO monad. After a lot of study and setting up a few functions, you can write lines of Haskell guaranteed to execute exactly once in the order of appearance, with errors handled appropriately.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Congratulations, you&amp;#8217;ve waded through large tracts of theory only to acheive what C programmers take for granted. Monads are undoubtedly a formidable weapon for pure functional programmers battling with side-effects, but what do imperative programmers care?&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;On the other hand, list comprehensions impress veteran coders of all stripes due to their astounding conciseness, and hence are a more universally appealing showpiece for monads. Consider &lt;a href="http://www.haskell.org/tutorial/monads.html"&gt;a simple example&lt;/a&gt;:&lt;/p&gt;&lt;/div&gt; &lt;div class="literalblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;[(x,y) | x &amp;lt;- [1,2,3], y &amp;lt;- [1,2,3], x /= y]&lt;/tt&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;which is:&lt;/p&gt;&lt;/div&gt; &lt;div class="literalblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;[(1,2),(1,3),(2,1),(2,3),(3,1),(3,2)]&lt;/tt&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Wouldn&amp;#8217;t it be great if C could do this? Our goal for this post is to get list comprehensions in C, preferably without learning anything about monads!&lt;/p&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;h2 id="_a_spoonful_of_syntactic_sugar"&gt;A spoonful of syntactic sugar&lt;/h2&gt; &lt;div class="sectionbody"&gt; &lt;div class="paragraph"&gt;&lt;p&gt;How do list comprehensions automatically try all combinations of x and y? Does Haskell have a special list comprehension parser that generates loops and stuff?&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Kind of. There is a special parser, but it merely converts our list comprehension into:&lt;/p&gt;&lt;/div&gt; &lt;div class="literalblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;do x &amp;lt;- [1,2,3]&lt;br /&gt;   y &amp;lt;- [1,2,3]&lt;br /&gt;   True &amp;lt;- return (x /= y)&lt;br /&gt;   return (x,y)&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;This hardly differs: some argue it is so close to the original that &lt;a href="http://www.haskell.org/haskellwiki/Syntactic_sugar/Cons"&gt;list comprehension should be rarely used&lt;/a&gt;. There are still no signs of any list operations, nor loops. We&amp;#8217;re no closer to solving the mystery.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;On the bright side, it now resembles C enough that we can consider translating it.&lt;/p&gt;&lt;/div&gt; &lt;/div&gt; &lt;h2 id="_let_me_c"&gt;Let me C&lt;/h2&gt; &lt;div class="sectionbody"&gt; &lt;div class="paragraph"&gt;&lt;p&gt;A first attempt:&lt;/p&gt;&lt;/div&gt; &lt;div class="literalblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;BEGIN_DO;&lt;br /&gt;  int a[] = { 1, 2, 3, 0 };&lt;br /&gt;  int b[] = { 1, 2, 3, 0 };&lt;br /&gt;  ASSIGN(x, a);&lt;br /&gt;  ASSIGN(y, b);&lt;br /&gt;  if (*x != *y) printf(" (%d, %d)", *x, *y);&lt;br /&gt;END_DO;&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;We&amp;#8217;ll figure out the macro definitions in a second. We&amp;#8217;re taking a few shortcuts here. We&amp;#8217;ve only translated the first two &lt;tt&gt;&amp;lt;-&lt;/tt&gt; expressions. We terminate the arrays with 0. And instead of returning a list of tuples, we simply print them out.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;If the &lt;tt&gt;ASSIGN&lt;/tt&gt; macros could change the above into two nested for loops then we&amp;#8217;d be done:&lt;/p&gt;&lt;/div&gt; &lt;div class="literalblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;for(*x = a; *x; x++) {&lt;br /&gt;  for(*y = b; *y; y++) {&lt;br /&gt;    if (*x != *y) printf(" (%d, %d)", *x, *y);&lt;br /&gt;  }&lt;br /&gt;}&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Unfortunately, the &lt;tt&gt;ASSIGN&lt;/tt&gt; lines are self-contained; if we translate them to for loops they&amp;#8217;ll stay unnested. Instead, we can use &lt;a href="http://gcc.gnu.org/onlinedocs/gcc-3.4.1/gcc/Labels-as-Values.html"&gt;computed goto&lt;/a&gt; to get the same effect:&lt;/p&gt;&lt;/div&gt; &lt;div class="literalblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;#define BEGIN_DO do { \&lt;br /&gt;void *stack[128]; \&lt;br /&gt;int stacki = 0; \&lt;br /&gt;stack[stacki] = &amp;amp;&amp;amp;label_end&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="literalblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;#define END_DO goto *stack[stacki]; label_end: ; } while(0)&lt;/tt&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class="literalblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;#define ASSIGN(x, a) \&lt;br /&gt;int *x, *x##next = a; \&lt;br /&gt;stack[++stacki] = &amp;amp;&amp;amp;label_##x; \&lt;br /&gt;label_##x: if (!*(x = x##next)) goto *stack[--stacki]; else x##next = x + 1&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;It looks horrible, and we needed GNU C extensions, but we&amp;#8217;ve hidden loops in the macros, just as Haskell must be doing.&lt;/p&gt;&lt;/div&gt; &lt;/div&gt; &lt;h2 id="_another_spoonful"&gt;Another spoonful&lt;/h2&gt; &lt;div class="sectionbody"&gt; &lt;div class="paragraph"&gt;&lt;p&gt;It turns out Haskell&amp;#8217;s &lt;tt&gt;do&lt;/tt&gt; and &lt;tt&gt;&amp;lt;-&lt;/tt&gt; are themselves syntactic sugar. Our example in fact expands again to:&lt;/p&gt;&lt;/div&gt; &lt;div class="literalblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;[1,2,3] &amp;gt;&amp;gt;= (\ x -&amp;gt; [1,2,3] &amp;gt;&amp;gt;= (\y -&amp;gt; return (x/=y) &amp;gt;&amp;gt;=&lt;br /&gt;   (\r -&amp;gt; case r of True -&amp;gt; return (x,y)&lt;br /&gt;                    _    -&amp;gt; fail "")))&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;This is unsweetened Haskell code. Still no loops, but we&amp;#8217;ve learned those separate lines of Haskell are really one big line: later lines are function evaluations nested within earlier lines.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Simulating nested for loops in C was on the right track after all. However, we should have used &lt;a href="http://gcc.gnu.org/onlinedocs/gcc-2.95.3/gcc_4.html#SEC65"&gt;nested functions&lt;/a&gt;. The extra flexibility and power should help complete the conversion.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;This time, we&amp;#8217;ll build a list of elements instead of just printing them. Also, let&amp;#8217;s replace the tuple (x,y) by the integer x+y so we can stick to integers and lists of integers; we can find a C equivalent for Haskell lists and tuples another day.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Intuition suggests all our functions should work with lists; then they&amp;#8217;ll look alike, which should make macros easy to write. This implies we must occasionally package integers in singleton lists. We wind up with:&lt;/p&gt;&lt;/div&gt; &lt;div class="listingblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;int *f0() {&lt;br /&gt;  int a[] = { 1, 2, 3, 0 };&lt;br /&gt;  int b[] = { 1, 2, 3, 0 };&lt;br /&gt;&lt;br /&gt;  int x;&lt;br /&gt;  auto int *f1();&lt;br /&gt;  {&lt;br /&gt;    int *r = list_0();&lt;br /&gt;    for(int *p = a; (x = *p); p++) r = list_cat(r, f1());&lt;br /&gt;    return r;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  int y;&lt;br /&gt;  auto int *f2();&lt;br /&gt;  int* f1() {&lt;br /&gt;    int *r = list_0();&lt;br /&gt;    for(int *p = b; (y = *p); p++) r = list_cat(r, f2());&lt;br /&gt;    return r;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  int z;&lt;br /&gt;  auto int *f3();&lt;br /&gt;  int* f2() {&lt;br /&gt;    int *r = list_0();&lt;br /&gt;    for(int *p = list_1(x != y); (z = *p); p++) r = list_cat(r, f3());&lt;br /&gt;    return r;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  int* f3() {&lt;br /&gt;    if (z) return list_1(x + y);&lt;br /&gt;    return list_0();&lt;br /&gt;  }&lt;br /&gt;}&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;where &lt;tt&gt;list_0&lt;/tt&gt; returns an empty list of integers, &lt;tt&gt;list_cat&lt;/tt&gt; concatenates two lists of integers, and &lt;tt&gt;list_1&lt;/tt&gt; creates a singleton list containing the given integer. The functions &lt;tt&gt;f0&lt;/tt&gt; and &lt;tt&gt;f1&lt;/tt&gt; take on the roles of the two nested for loops from our previous attempt, only now they also concatenate the values they compute.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Because forward-declaring functions is difficult with macros, we introduce an array of function pointers so one function can call the next. Also, since C doesn&amp;#8217;t do pattern matching, we&amp;#8217;ll cheat with an ad hoc macro. The full program:&lt;/p&gt;&lt;/div&gt; &lt;div class="listingblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;#include &amp;lt;string.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#define BEGIN_DO ({ \&lt;br /&gt;  int *(*fun[64])(int); \&lt;br /&gt;  int funi = 0&lt;br /&gt;&lt;br /&gt;#define END_DO fun[0](0); \&lt;br /&gt;})&lt;br /&gt;&lt;br /&gt;#define ASSIGN(x, a) int x; \&lt;br /&gt;int *x##fun(int i) { \&lt;br /&gt;  int *r = list_0(); \&lt;br /&gt;  for(int *p = a; (x = *p); p++) r = list_cat(r, fun[i+1](i+1)); \&lt;br /&gt;  return r; \&lt;br /&gt;} \&lt;br /&gt;fun[funi++] = x##fun&lt;br /&gt;&lt;br /&gt;#define MATCH(x, a) int* match() { \&lt;br /&gt;  if (x) return a; \&lt;br /&gt;  return list_0(); \&lt;br /&gt;} \&lt;br /&gt;fun[funi++] = match&lt;br /&gt;&lt;br /&gt;int *list_0() {&lt;br /&gt;  int *r = malloc(sizeof(*r));&lt;br /&gt;  *r = 0;&lt;br /&gt;  return r;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int list_len(int *a) {&lt;br /&gt;  int *x = a;&lt;br /&gt;  while(*x) x++;&lt;br /&gt;  return x - a;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int *list_cat(int *a, int *b) {&lt;br /&gt;  int m = list_len(a), n = list_len(b);&lt;br /&gt;  int *r = realloc(a, sizeof(*r) * (m + n + 1));&lt;br /&gt;  memcpy(r + m, b, sizeof(*r) * n);&lt;br /&gt;  r[m+n] = 0;&lt;br /&gt;  free(b);&lt;br /&gt;  return r;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int *list_1(int n) {&lt;br /&gt;  int *r = malloc(sizeof(*r) * 2);&lt;br /&gt;  *r = n;&lt;br /&gt;  r[1] = 0;&lt;br /&gt;  return r;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int main() {&lt;br /&gt;  int *r = BEGIN_DO;&lt;br /&gt;    int a[] = { 1, 2, 3, 0 };&lt;br /&gt;    int b[] = { 1, 2, 3, 0 };&lt;br /&gt;    ASSIGN(x, a);&lt;br /&gt;    ASSIGN(y, b);&lt;br /&gt;    ASSIGN(z, list_1(x != y));  // Memory leak.&lt;br /&gt;    MATCH(z, list_1(x + y));&lt;br /&gt;  END_DO;&lt;br /&gt;&lt;br /&gt;  // Should be 3 4 3 5 4 5.&lt;br /&gt;  for(int *x = r; *x; x++) printf(" %d", *x);&lt;br /&gt;  putchar('\n');&lt;br /&gt;  free(r);&lt;br /&gt;  exit(0);&lt;br /&gt;}&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;At last! List comprehensions in C.&lt;/p&gt;&lt;/div&gt; &lt;/div&gt; &lt;h2 id="_haskell_8217_s_list_monad"&gt;Haskell&amp;#8217;s list monad&lt;/h2&gt; &lt;div class="sectionbody"&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Haskell does not need zany macros. Instead, it has:&lt;/p&gt;&lt;/div&gt; &lt;div class="literalblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;instance Monad [] where&lt;br /&gt;  return a = [a]&lt;br /&gt;  xs &amp;gt;&amp;gt;= f = concat (map f xs)&lt;br /&gt;  fail _ = []&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Mystery solved. The &lt;tt&gt;&amp;gt;&amp;gt;=&lt;/tt&gt; operator is like our &lt;tt&gt;ASSIGN&lt;/tt&gt; macro: they both compute a function on each element of a list and concatenate all the results. The &lt;tt&gt;return&lt;/tt&gt; function is our &lt;tt&gt;list_1&lt;/tt&gt; function, and &lt;tt&gt;fail&lt;/tt&gt; is our &lt;tt&gt;list_0&lt;/tt&gt;.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;In both languages, we made looping over lists invisible by writing a routine that converts an integer into a list (just put it in a list of size one) and another that runs a function on lists of integers even though the function normally takes integers (just run the function on each integer in the list, then concatenate the results).&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;We can generalize this idea to hide other kinds of repetitive code. This is precisely what Haskell monads are, and why do notation exists. Roughly speaking, we augment the abilities of existing functions by describing how to convert a regular output into a new and improved type (&lt;em&gt;monadic type&lt;/em&gt;) and how to get a function to operate on monadic types when it expects the regular type.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;It&amp;#8217;s cool that we can get the same effect in C. Nevertheless:&lt;/p&gt;&lt;/div&gt; &lt;div class="ulist"&gt;&lt;ul&gt; &lt;li&gt; &lt;p&gt; Haskell has a few lines of robust, clean code instead of fragile, ugly macros. &lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt; This design may never occur to a C programmer, while monads are ubiquitous in Haskell. &lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt; Monads mesh well with pattern matching, which C lacks. &lt;/p&gt; &lt;/li&gt; &lt;/ul&gt;&lt;/div&gt; &lt;/div&gt; &lt;h2 id="_a_comprehensions_compromise"&gt;A comprehensions compromise&lt;/h2&gt; &lt;div class="sectionbody"&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Our macros were challenging to write because their combined output must be nested. It&amp;#8217;s why we needed computed goto and a stack of labels when simulating nested for loops, and why we needed an array of function pointers when we moved to nested functions.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;It would be easier if we tolerated nesting:&lt;/p&gt;&lt;/div&gt; &lt;div class="listingblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;int* assign(int *x, int *a, int *(*fun)()) {&lt;br /&gt;  int *r = list_empty();&lt;br /&gt;  for(int *p = a; (*x = *p); p++) r = list_cat(r, fun());&lt;br /&gt;  return r;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int *lamb() {&lt;br /&gt;  int a[] = { 1, 2, 3, 0 };&lt;br /&gt;  int b[] = { 1, 2, 3, 0 };&lt;br /&gt;  int x;&lt;br /&gt;  auto int *lamb();&lt;br /&gt;  return assign(&amp;amp;x, a, lamb);&lt;br /&gt;  int *lamb() {&lt;br /&gt;    int y;&lt;br /&gt;    auto int *lamb();&lt;br /&gt;    return assign(&amp;amp;y, b, lamb);&lt;br /&gt;    int *lamb() {&lt;br /&gt;      int z;&lt;br /&gt;      auto int *lamb();&lt;br /&gt;      return assign(&amp;amp;z, list_1(x != y), lamb);&lt;br /&gt;      int *lamb() {&lt;br /&gt;        return z ? list_1(x + y) : list_empty();&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;int *r = lamb();&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;The code is verbose, because at several points, one line declares a function, another calls it, and a third defines it. All three would coalesce into a single line if C had lambda expressions. Frustratingly, GNU C supports closely related nested functions but not anonymous functions. Why not? Even &lt;a href="http://en.wikipedia.org/wiki/C%2B%2B0x#Lambda_functions_and_expressions"&gt;standard C++ is getting lambda expressions&lt;/a&gt;! However, if we tolerate one macro, we can ease the pain:&lt;/p&gt;&lt;/div&gt; &lt;div class="listingblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;#define ASSIGN(x, a) \&lt;br /&gt;  int x; \&lt;br /&gt;  auto int *lamb(); \&lt;br /&gt;  return assign(&amp;amp;x, a, lamb); \&lt;br /&gt;  int *lamb()&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;  int *lamb() {&lt;br /&gt;    int a[] = { 1, 2, 3, 0 };&lt;br /&gt;    int b[] = { 10, 20, 30, 0 };&lt;br /&gt;    ASSIGN(x, a) {&lt;br /&gt;      ASSIGN(y, b) {&lt;br /&gt;        ASSIGN(z, list_1(x != y)) {&lt;br /&gt;          return z ? list_1(x + y) : list_empty();&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  int *r = lamb();&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;While messier than Haskell, it is still readable. Moreover, the nested braces may aid seasoned imperative programmers, who might incorrectly think the Haskell do notation represents statements executing one after another. Here, it is unmistakeably clear we are nesting function calls.&lt;/p&gt;&lt;/div&gt; &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-7442732949739965367?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/7442732949739965367/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=7442732949739965367' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7442732949739965367'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7442732949739965367'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2011/04/list-comprehensions-in-c.html' title='List Comprehensions in C'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-1586267961046193575</id><published>2011-04-06T10:20:00.001-07:00</published><updated>2011-04-12T01:35:10.113-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Convert line breaks? No!</title><content type='html'>&lt;a href="http://benlynn.blogspot.com/2007/02/new-beginning_21.html"&gt;When I moved my blog
to this site&lt;/a&gt;, I left the default settings alone. Unfortunately one of them was
"Convert line breaks". While harmless at first, it eventually caused headaches
when editing HTML for fancier formatting. Turning off the option destroyed all
paragraph breaks in older posts, so I had to leave it on for their sake.&lt;br /&gt;
I believe Blogger no longer has this problem: on a new test blog, flipping the
switch appeared to have no effect on existing posts. However, my blog predates
  this change.&lt;br /&gt;
&lt;a href="http://www.google.com/search?q=convert+line+breaks+blogger"&gt;Googling&lt;/a&gt; yields
many reports of trouble with this maleficent option. Some suggest a laborious
method of disabling it: turn it off, then break paragraphs in old posts by
hand!&lt;br /&gt;
Such advice was probably written before Blogger gained the export and import
features, which are the key to a simpler but scarier solution:&lt;br /&gt;
&lt;ol type="1"&gt;
&lt;li&gt;
Export the blog, from the "Basic" tab under "Settings".
&lt;br /&gt;

&lt;/li&gt;
&lt;li&gt;
In the "Edit Posts" tab under "Posting", select all posts.
&lt;br /&gt;

&lt;/li&gt;
&lt;li&gt;
Delete them all!
&lt;br /&gt;

&lt;/li&gt;
&lt;li&gt;
import the blog and publish all posts, again from "Basic" tab under "Settings".
&lt;br /&gt;

&lt;/li&gt;
&lt;/ol&gt;
Now you can say no to  "Convert line breaks" without trashing ancient posts,
though when editing new posts, you still have to select "Use &amp;lt;br /&amp;gt; tags" under
"Post Options" to get the desired behaviour.&lt;br /&gt;
It works because the export format robustly represents line breaks with HTML
tags whether or not the evil option was on when the post was written. Take
care: after I exported my blog, I got distracted by the recently added "Spam"
tab under "Comments", and found a non-spam comment sequestered within. I freed
it, but forgot to re-export my blog, so after I deleted all posts and imported
my blog, the comment was lost forever.&lt;br /&gt;
The export format has also given me ideas for
&lt;a href="http://benlynn.blogspot.com/2009/06/asciidoc-to-blogger.html"&gt;my script that
publishes HTML posts via the Blogger Data API&lt;/a&gt;, which has a hard time
preserving formatted source code. Unfortunately, frequent experiments have
temporarily barred my script:&lt;br /&gt;
&lt;pre&gt;Blog has exceeded rate limit or otherwise requires word verification for new posts&lt;/pre&gt;
&lt;p&gt;I’ll try again another day.&lt;/p&gt;
&lt;h2&gt;Permalink Pain&lt;/h2&gt;
&lt;p&gt;
Unfortunately, I belatedly discovered serious manual editing is still required. Permalinks have changed; they now put the day of the month in the URL. Restoring old entries generates the new kind of permalink, invalidating all existing links to old posts.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-1586267961046193575?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/1586267961046193575/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=1586267961046193575' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1586267961046193575'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1586267961046193575'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2011/04/convert-line-breaks-no_06.html' title='Convert line breaks? No!'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-1031880622436707976</id><published>2011-04-03T17:07:00.001-07:00</published><updated>2011-04-12T01:36:18.018-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Tagline-Oriented Programming</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;&lt;div id='preamble'&gt;&lt;div class='sectionbody'&gt;&lt;div class='paragraph'&gt;&lt;p&gt;In the land of "tl;dr", the one-liner is king. Short is sweet, and also highly contagious. A paragraph is easy to ignore, while a good tagline embeds itself in a reader’s mind against their will, spreading independently of the accompanying product.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;In programming, some one-liners are equally alluring. There is only room for the bare essentials: we must distill the purest form of solutions to problems. Their brevity makes them memorable, and awe-inspiring, as shrinking code can take considerable skill; most recently, I’ve been stunned by &lt;a href='http://home.hccnet.nl/h.g.muller/max-src2.html'&gt;a 1433-character chess engine in C&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;a href='http://benlynn.blogspot.com/2010/07/timeless-beauty-of-shell-scripts_31.html'&gt;Having already praised Bash one-liners&lt;/a&gt;, this time I’m taking a shallow look at Haskell, and in particular, a trivial function:&lt;/p&gt;&lt;/div&gt;&lt;div class='listingblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;emp :: a -&amp;gt; ([b] -&amp;gt; a) -&amp;gt; [b] -&amp;gt; a&lt;br /&gt;emp a f [] = a&lt;br /&gt;emp a f (x:xs) = f (x:xs)&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Or since we’re advocating one-liners:&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;emp a f x = if null x then a else f x&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Why? In Haskell, recursion on lists often goes like this:&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;f []     = empty_list_case&lt;br /&gt;f (x:xs) = recurse_somehow x f(xs)&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;The &lt;tt&gt;emp&lt;/tt&gt; function let us combine these two lines into one. The fold family of functions is similar: for them, the 2nd argument handles the empty-list case, while the 1st argument is a function that describes how to process the next element. However, they are insufficiently general for our purposes.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Should we go to this trouble just to squeeze the empty-list case into the same line as the inductive step? Yes, if you share my appreciation for one-liners! Also:&lt;/p&gt;&lt;/div&gt;&lt;div class='ulist'&gt;&lt;ul&gt;&lt;li&gt; &lt;p&gt;this is a natural extension of the precedent set by the fold family &lt;/p&gt;&lt;/li&gt;&lt;li&gt; &lt;p&gt;it’s harder to forget about the base case &lt;/p&gt;&lt;/li&gt;&lt;li&gt; &lt;p&gt;single lines suit interactive Haskell (if you do type these in, remember to add "let" in front of the definitions). &lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id='_examples'&gt;Examples&lt;/h2&gt;&lt;div class='sectionbody'&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Here’s &lt;tt&gt;foldl&lt;/tt&gt; and &lt;tt&gt;foldr&lt;/tt&gt; in one line each:&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;foldr f z = emp z $ \(x:xs) -&amp;gt; f x (foldr f z xs)&lt;br /&gt;foldl f z = emp z $ \(x:xs) -&amp;gt; foldl f (f z x) xs&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;And the membership test:&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;elem x = emp False $ \(y:ys) -&amp;gt; x==y || (x `elem` ys)&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;All subsequences (though in an order differing from &lt;tt&gt;Data.List.subsequences&lt;/tt&gt;):&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;sub = emp [[]] $ \(x:xs) -&amp;gt; sub xs ++ (map (x:) (sub xs))&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;How about all permutations? In C, where we can loop through an array and swap elements in place, an easy but impure recursive solution takes every element in turn, temporarily swaps it to the first position, then calls itself on the remainder of the elements, where the lowest level of recursion prints the whole buffer.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;In Haskell, I found it easiest to do something similar by maintaining 2 lists on either side of a selected element. My first attempt came out as:&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;-- Invoke with: p "" "" "abc"&lt;br /&gt;p a [] [] = [a]&lt;br /&gt;p [] ys [] = []&lt;br /&gt;p a ys [] = map (a++) (p [] [] ys)&lt;br /&gt;p a ys (x:xs) = (if null a then (p [x] ys xs) else []) ++ (p a (x:ys) xs)&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;It’s unclear how to rewrite this in one line. Perhaps it’s best to break the problem into two sub-problems to get one-liners. The following is based on &lt;a href='http://www.haskell.org/pipermail/haskell-cafe/2002-June/003122.html'&gt;elegant code found on a mailing list&lt;/a&gt;:&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;f = emp [] $ \(x:xs) -&amp;gt; (x,xs):[(y,x:ys) | (y,ys) &amp;lt;- (f xs)]&lt;br /&gt;perm = emp [[]] $ \xs -&amp;gt; [y:zs | (y,ys) &amp;lt;- (f xs), zs &amp;lt;- (perm ys)]&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;The helper function &lt;tt&gt;f&lt;/tt&gt; is the FP version of iterating through a list and plucking an element out, and has uses outside this problem.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id='_integers'&gt;Integers&lt;/h2&gt;&lt;div class='sectionbody'&gt;&lt;div class='paragraph'&gt;&lt;p&gt;A similar definition for integers is also handy:&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;induct a f x = if 0==x then a else f&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;For starters, we can define &lt;tt&gt;emp&lt;/tt&gt; with &lt;tt&gt;induct&lt;/tt&gt;:&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;emp a f = induct a f . length&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;List all n-letter strings consisting of the letters in "BEN":&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;f = induct [""] $ \n -&amp;gt; [x:xs | x &amp;lt;- "BEN", xs &amp;lt;- f (n-1)]&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Compute an n-bit Gray code:&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;g = induct [""] $ \n -&amp;gt; (map ('0':)) (g (n-1)) ++ (map ('1':)) (reverse (g (n-1)))&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Though shallow, Haskell would gain from default &lt;tt&gt;induct&lt;/tt&gt; and &lt;tt&gt;emp&lt;/tt&gt; functions. (Perhaps there already are? I don’t know Haskell that well.) They bring the length of many interesting function defintions under a mystical threshold that makes them catchy and appealing.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-1031880622436707976?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/1031880622436707976/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=1031880622436707976' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1031880622436707976'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1031880622436707976'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2011/04/tagline-oriented-programming_03.html' title='Tagline-Oriented Programming'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-6995552818708103858</id><published>2011-03-27T01:50:00.001-07:00</published><updated>2011-04-06T01:49:02.251-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>CMU -= OOP, CMU++;</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;&lt;div id='preamble'&gt;&lt;div class='sectionbody'&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I should have been a comedian. I have excellent timing.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I applied for Computer Science grad school during the irrationally exuberant dot-com boom, when much of my competition ran off chasing easy money. During the subsequent crash I was already a sheltered student. By the time I finished, the economy was up again. My lucky streak continued as I managed to secure a job before the global financial crisis struck.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;However, I’m most thankful that I was amongst the last batch of students of my undegraduate CS program who were only taught object-oriented programming in an optional course.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;It wasn’t just my alma mater. According to &lt;a href='http://itmanagement.earthweb.com/career/article.php/3761921/The-Anti-Java-Professor-and-the-Jobless-Programmers.htm'&gt;an interview with a CS professor emeritus at NYU&lt;/a&gt;, around that time, many universities bowed to pressure to switch to Java and focus less on fundamentals. This produced less capable programmers, or as the article puts it, “today’s Java-savvy college grad is tomorrow’s pizza-delivery man”. Again, my competition was decimated merely because of timing.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Apart from increased job security, I’m grateful I learned less object-oriented nonsense and more CS, as I’m deeply interested in the subject. In fact, this is why I chose to study CS; I’m just fortunate I happen to live in a time and place where coding is a marketable skill.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Now that I can afford to be less selfish, I wish universities would undo this injustice and &lt;strong&gt;remove object-oriented programming from introductory CS curriculums&lt;/strong&gt;, so future students can benefit, just as &lt;a href='http://existentialtype.wordpress.com/2011/03/15/teaching-fp-to-freshmen/'&gt;Carnegie Mellon University is doing&lt;/a&gt;. CMU's decision inspired me to post this entry, and update &lt;a href='http://cs.stanford.edu/~blynn/c/ch02.html'&gt;my page denouncing object-oriented programming&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id='_business'&gt;Business&lt;/h2&gt;&lt;div class='sectionbody'&gt;&lt;div class='paragraph'&gt;&lt;p&gt;While some applaud the move, others state that learning a language and programming paradigm rarely seen in industry leaves a student ill-prepared.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;My own experience suggests otherwise. I have little trouble coding in new programming languages so long as they’re not &lt;a href='http://en.wikipedia.org/wiki/INTERCAL'&gt;too esoteric&lt;/a&gt;. Granted, the first few lines are painful, as I’ll have to constantly look up keywords in a reference while acclimatizing to error messages and other peculiarities of the development environment. But after a while, I’ll pick up the syntax for arithmetic, loops, conditionals, function calls, and so on, and that’ll be enough to keep me going. I might miss out on idioms, but I can get my point across.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;The difficult part is designing the underlying data structures and algorithms. That’s where CS is most magical. Functional programming gives me an edge. It makes recursion less scary, and recursion is often a great approach to a problem: sorting, parsing, anything with trees. Also, experience with lazy evaluation increases my awareness of parallelism.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I believe it also makes my code cleaner, because treating functions as first-class citizens opens my eyes to design choices I might otherwise miss. It’s hard to say: a Java fanatic might be trained to spot perfect places for anonymous inline classes.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;In interviews, I let candidates choose the language as I’m more interested in the idea being expressed. Some candidates write code comfortably, but struggle with relatively simple tasks because they can’t find the right algorithm or data structure. They’re like an author with perfect spelling but who only know boring stories.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Others quickly find a good solution, but their code may have syntax issues here and there. These mistakes hardly bother me because after a week of seeing the same compiler error messages over and over again, their syntax will be as good as mine.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id='_personal'&gt;Personal&lt;/h2&gt;&lt;div class='sectionbody'&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Although OOP was optional for me, I studied it with relish. I read “Design Patterns” cover to cover shortly after purchasing it. Bertrand Meyer’s words were gospel. Eiffel, a pure OO language, became my favourite. I promoted OOP more fiercely than I attack it now; back then I was even more obstinate. I believed the only path to virtues such as modularity, information hiding, and code reuse was through OOP, partly because I thought OOP folks invented these concepts.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Over the years, events caused me to pause and reexamine what I had been taught. Many lines of code later, I finally discarded OOP and freed myself. I felt cheated, which is why I’m more emotionally attached to this stuff than one might expect.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;There’s no harm in learning OOP as long as you avoid my mistake and arm yourself with a healthy dose of skepticism (which you should do all the time anyway, including now).&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-6995552818708103858?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/6995552818708103858/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=6995552818708103858' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/6995552818708103858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/6995552818708103858'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2011/03/cmu-oop-cmu_27.html' title='CMU -= OOP, CMU++;'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-5278202426317204620</id><published>2011-02-07T20:51:00.001-08:00</published><updated>2011-04-06T01:49:02.264-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>UTF-8 good, UTF-16 bad</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;&lt;div id='preamble'&gt;&lt;div class='sectionbody'&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Lately I’ve been programming in Go. I started by writing &lt;a href='http://cs.stanford.edu/~blynn/c2go/'&gt;a few popular Unix tools in Go&lt;/a&gt;, before moving on to &lt;a href='http://cs.stanford.edu/~blynn/nex/'&gt;a lexer that supports structural regular expressions and UTF-8&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Unicode support was easy to add because Go lives and breathes UTF-8. But why did the Go designers favour UTF-8, and not UTF-16? Widespread software such as Windows, Java, and Python use UTF-16 internally, so why not Go?&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Further reading revealed the answer. UTF-16 is a historical accident that persists mainly due to inertia. UTF-16 has no practical advantages over UTF-8, and it is worse in some ways. The Go designers knew what they were doing, so they chose UTF-8.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id='_down_with_utf_16'&gt;Down with UTF-16&lt;/h2&gt;&lt;div class='sectionbody'&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Before, from vague hunches, I had thought that UTF-16 was simpler because each character took exactly 2 bytes (so it handled only a strict subset of Unicode), and that UTF-16 was more compact for certain languages.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;The first statement is patently false. UTF-16 is a variable-length encoding, and hence not much simpler than UTF-8. As for the second: consider HTML. The amount of whitespace and tags in a typical document is high enough that UTF-8 is more compact than UTF-16. This generalizes to other formats, such as Go source code.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;So there’s no upside. But there are plenty of downsides to UTF-16:&lt;/p&gt;&lt;/div&gt;&lt;div class='olist arabic'&gt;&lt;ol class='arabic'&gt;&lt;li&gt; &lt;p&gt;We must worry about byte ordering because UTF-16 deals with 16 bits at a   time. A related drawback is the loss of self-synchronization at the byte   level. &lt;/p&gt;&lt;/li&gt;&lt;li&gt; &lt;p&gt;We lose backwards compatibility with ASCII. &lt;/p&gt;&lt;/li&gt;&lt;li&gt; &lt;p&gt;We lose backwards compatibility with code treating NUL as a string   terminator. &lt;/p&gt;&lt;/li&gt;&lt;li&gt; &lt;p&gt;We cannot losslessly convert from byte streams. &lt;/p&gt;&lt;/li&gt;&lt;li&gt; &lt;p&gt;The encoding is inflexible: hopefully 0x110000 code points should be   enough, but if it were extended (again), UTF-16 would need major surgery. In   contrast, by simply allowing up to 6 bytes, UTF-8 can handle up to 2&lt;sup&gt;31&lt;/sup&gt; code   points. &lt;/p&gt;&lt;/li&gt;&lt;li&gt; &lt;p&gt;Ugly encoding: it’s the reason why U+D800 to U+DFFF is reserved   (though thankfully UTF-16 is self-synchronizing with 16-bit granularity). &lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I tired of thinking about the drawbacks of UTF-16, but there are likely more. Thus even if we only worked with a strange set of documents where UTF-16 is more efficient, the savings are too small to justify the complexity.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id='_a_living_dead_standard'&gt;A living dead standard&lt;/h2&gt;&lt;div class='sectionbody'&gt;&lt;div class='paragraph'&gt;&lt;p&gt;How did so many projects settle on such a horrible standard? It’s a sad story. The original Unicode supported exactly 0x10000 code points, making UCS-2 (the precursor to UTF-16) attractive. Each character neatly corresponded to one 16-bit word. Although UTF-8 might still be preferable thanks to byte ordering and ASCII compatibility, UCS-2 is certifiably sane. People built systems based on UCS-2, and rejoiced that the codepage Stone Age was over.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Then an unnatural disaster struck: the Unicode Consortium decided more code points were needed. The supple UTF-8 got away unscathed, but there’s no way to squeeze more than 16 bits into UCS-2. Apart from abandoning UCS-2, the only recourse was to reserve a chunk of values for encoding the higher code points. Thus UTF-16 was born. Hopefully, developers could simply tweak masses of existing UCS-2 code for the new standard. Sadly, &lt;a href='http://stackoverflow.com/questions/1049947/should-utf-16-be-considered-harmful'&gt;various UTF-16 bugs&lt;/a&gt; suggest that much code remains unaware UTF-16 is a variable-length encoding.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Despite the trauma it would cause, they should have killed UCS-2 when they had the chance. Now we have a grotesque encoding scheme that shambles alongside UTF-8.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id='_gauss_was_here'&gt;Gauss was here&lt;/h2&gt;&lt;div class='sectionbody'&gt;&lt;div class='paragraph'&gt;&lt;p&gt;According to Wikipedia, UTF-8 was built on ASCII by Rob Pike and Ken Thompson. ASCII was developed from telegraph codes by a committee. The telegraph codes stemmed from a Western Union code, which evolved from a code by Donald Murray, who modified a 5-bit code invented by Émile Baudot, who based his work on a code designed by Carl Friedrich Gauss and Wilhelm Weber. It’s awe-inspiring that the Prince of Mathematicians was involved. And if he were alive today, I bet he’d disparage UTF-16!&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-5278202426317204620?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/5278202426317204620/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=5278202426317204620' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5278202426317204620'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5278202426317204620'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2011/02/utf-8-good-utf-16-bad_07.html' title='UTF-8 good, UTF-16 bad'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-3298006860052526977</id><published>2010-10-12T00:38:00.001-07:00</published><updated>2011-04-06T01:49:02.267-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><title type='text'>Spooked by a corrupt initrd image</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Halloween is approaching. It is apt I have a scary tale to tell.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I left my laptop on in the sun, on a surface with poor conductivity. As I opened it, I glimpsed it complaining of heat exhaustion before it powered off. (That’s happened to me before, too!) I let it cool off, but on booting Ubuntu, a terrifying message was all that appeared on the screen:&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;Error 16: Inconsistent filesystem structure&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Uh oh. Did I lose everything? What haven’t I backed up lately? Indeed, what have I backed up lately?&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I searched online for the error message, and took heart when others reported no data loss in various forums. Running fsck was suggested, though in at least one case they resorted to reinstalling the OS. No way I’m doing that!&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;My first instinct was to dust off my USB DVD writer and burn a &lt;a href='http://ubuntu-rescue-remix.org/'&gt;Ubuntu rescue CD&lt;/a&gt;. This turned out to be unnecessary, but at least I was reassured when I used it to mount the Linux partition and found my files intact. Firing up fsck had no effect: I was still defeated by the same error message.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Playing with the GRUB command-line (by pressing “c” instead of selecting an kernel to boot) showed that the initrd image was corrupted, as selecting the newest image with the “initrd” command triggered the same error message.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;The solution was easy. Boot up an older kernel, then download a fresh copy of the initrd image by running:&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;$ sudo apt-get install --reinstall linux-image-2.6.32-25-generic&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-3298006860052526977?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/3298006860052526977/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=3298006860052526977' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3298006860052526977'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3298006860052526977'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2010/10/spooked-by-corrupt-initrd-image_12.html' title='Spooked by a corrupt initrd image'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-4226227260728159733</id><published>2010-10-07T21:36:00.001-07:00</published><updated>2011-04-06T01:49:02.270-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='shamelessplug'/><title type='text'>Shameless plugs</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;&lt;div id='preamble'&gt;&lt;div class='sectionbody'&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Better the head of a chicken than the tail of an ox. This Chinese proverb captures entrepreneurial thoughts I’ve long harboured. It’s a common affliction around my neck of the woods.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Unfortunately, I’m disinclined to abandon my cushy job to work my fingers to the bone for an uncertain reward. My dreams are likely doomed to remain dreams.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;To compensate, I’m living vicariously through friends who are being their own boss. I’m devoting this post to them; may it contribute at least an iota to their success.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;&lt;a href='http://cltn.net/'&gt;China Luxury Travel Network (CLTN.net)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Ethan lived on the ground floor of my apartment at Stanford. Early in my degree, he finished his Masters, in financial mathematics I think. I’m uncertain because we rarely chatted about school! I’m sure his latest venture will succeed, and not just because he’s capable.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I was in Ottawa for a conference in '03. I skipped half of it to hang out with Ethan, who gave me a tour of the city where he grew up, which became challenging when a widespread blackout hit the region. On top of all this, I had FedExed a visa document to his parent’s house (which I needed to return to the US), and he went out of his way to deliver it to me.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;A few years later, I was in China on vacation, mainly because I wanted to see the Guangzhou Trade Fair. I met up with Ethan. I thought we’d do little more than catch up over a meal. Well, we did chat over dinner: Ethan had worked in Hong Kong’s finance sector for a while, and was now living off savings while exploring China. I was still a starving student.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;But then Ethan took us to a health spa in Shenzhen which was cheaper yet far more luxurious than the hotels we had been considering. For about $20 a day I got massages, food, spas, haircuts, movies, a place to sleep, etc. I never thought I’d get a pedicure in my life, but because it was effectively free, I had to try it.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Then in Hong Kong, we crashed at his place, which saved a lot of money. He took us on a whirlwind shopping and eating tour. Soon after, we had to part ways, but not before he showed us a website offering discounted rates at hotels in Guangzhou. We got a great deal at a great hotel across the street from the trade fair. At the check-in counter, the guy in front of us was livid when he learned how little we were paying.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;He offered to take me on a tour of China after the trade fair was over, but I declined. I deeply regret my decision. What’s a few weeks of missed grad school? Ethan spoke three or more Chinese languages. He knew where to go. He had contacts everywhere. Clearly, the China luxury travel business is perfect for him.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;&lt;a href='http://embraceglobal.org/'&gt;Embrace&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Rahul lived a few apartments away from me in grad school. He mysteriously vanished one year. I heard later it was because he was “saving babies”. This raised yet more questions! Wasn’t he an engineer?&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Certain problematic births are far less problematic when an incubator is available. Unfortunately, the cost of typical incubators often places them beyond the reach of poor remote villages, contributing to higher infant mortality. Rahul helped design a vastly cheaper incubator, and hopes to eventually supply one wherever babies are born.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;&lt;a href='http://www.totalphase.com/'&gt;Total Phase&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I met Kumaran through his wife. I was a Teaching Assistant for an introductory computer science class she was taking. Kumaran wanted to meet partly because he wanted help recruiting people for his company. I was of no help: at the time, my engineer friends were already employed, and they were busy recruiting people themselves!&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Total Phase’s biggest hit is a USB protocol analyzer, an invaluable tool when developing USB products. Kumaran loves being an engineer designing for engineers. They never bothered advertising since engineers tend to use logic to determine purchases, and their products are better and cheaper than those of their competitors. I saw parallels when I read about the HP200A, an audio oscillator that was profitable because it was the best and the cheapest.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;&lt;a href='http://www.disceraser.com/'&gt;Disc Eraser&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;My cousin Wade designed a simple tool to render CDs and DVDs unreadable. An easy motion etches deep parallel scratches into the media: Freddy Krueger for discs. Quick and safe. No batteries. No electricity. The disc stays in one piece, ready for recycling.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;He used to work on hard drive platters at IBM (before they sold the division to Hitachi) so he knows what he’s doing. He verified his claims with AcoDisc, a data recovery company. After a single application of the Disc Eraser to a CD, the data was deemed unrecoverable.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I don’t burn much, let alone anything confidential, so it’s not something I need. However, I’m grateful for a sample he gave me: as a kind of therapy, I savagely mutilate CDs I receive as junk mail.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;&lt;a href='http://bu.mp/'&gt;Bump&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Dave was yet another electrical engineering student in my neighbourhood during grad school. I never would have guessed he would join the dark side and become a Pointy-haired Boss of a startup. Seriously though, I like tech companies with an engineer at the helm. And Dave’s hair is not pointy.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Years ago, a friend once pointed out that two people in the same room cannot easily get their laptops to talk to each other. Sure, there’s Bluetooth, ad-hoc wireless networking, and other hacks, but he was right: even now, it’s easiest for one party to verbally ask for contact details of the other. This is somehow troubling.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I remember a light-hearted news report I watched once, which described a gadget in Japan. You’d enter a few personal details, and carry it around. It would alert you if someone of the opposite sex nearby had the same gadget and had similar interests to you. What became of this? Couldn’t they do something like this so you would never have to spell out someone’s email address or type in a phone number?&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Enter Bump, an app for iPhone and Android that attacks this problem in a cute way. Both sides run the app, then lightly bump their phones together to swap contact details, or other data. No physical contact is actually needed as the phones just have to be close enough, but smashing your fists together is more macho!&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;&lt;a href='http://www.etsy.com/shop/kestralia'&gt;Millinery Kestralia&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Kestrel lives a few streets away, which benefits me greatly because she is a good chef and a keen gardener. She also loves making hair accessories, and recently started selling them. Each handcrafted piece is unique and beautiful, like their creator.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-4226227260728159733?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/4226227260728159733/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=4226227260728159733' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4226227260728159733'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4226227260728159733'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2010/10/shameless-plugs_07.html' title='Shameless plugs'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-635135255105363499</id><published>2010-09-18T12:46:00.001-07:00</published><updated>2011-04-12T01:42:20.279-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Moore's Treadmill</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;&lt;div id='preamble'&gt;&lt;div class='sectionbody'&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Early in my PhD, I told my advisor I had implemented a particular cryptographic algorithm with a tolerable running time. It wasn’t that fast, but I figured &lt;a href='http://en.wikipedia.org/wiki/Moore’s_law'&gt;within 2 years, microchips would be twice as powerful&lt;/a&gt;, so my code might be practical even on handheld devices.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;He replied: "No. Then they’ll halve the power consumption to double the battery life." Or they’ll want to run it on yet smaller devices. Laptops, phones, smart cards. I still had plenty of work to do.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;For me, user interfaces are trapped on the same treadmill.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;Less is more&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Not so long ago, my desktop had a 14-inch CRT display, a lonely and lowly processor clocked at 200MHz, a few gigs of hard disk space, and a 14.4k net connection. The dearth of screen real estate favoured spartan user interfaces, the CPU struggled unless tools were quick and nimble, and the cramped conditions of the hard disk and net connection favoured good things in small packages.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;For example, when the time came to take sides in the &lt;a href='http://en.wikipedia.org/wiki/Editor_war'&gt;text editor holy war&lt;/a&gt;, I chose vi because it would only take several minutes to grab a few hundred kilobytes. Emacs was over 6 megabytes.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Now I often work at a PC sporting multiple cores, each over 20 times faster than my old workhorse. I have multiple monitors, each so large that I turn my head to look at different parts of a screen. Network latency is low, and bandwidth is high.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Yet I remain minmalist. Indeed, I keep finding new ways to trim the fat, despite having ample space. For instance, my first web browser had big buttons and a plethora of bars: title bar, menu bar, bookmarks bar, status bar, location bar, etc. Today, even though I can afford extra fluff, &lt;a href='http://www.google.com/chrome'&gt;my primary browser&lt;/a&gt; shows little apart from the webpage: a bar for tabs (itself a space-saving measure over multiple windows), a single textbox, and a handful of buttons.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I chose my style partly because I’m a programmer to the bone: my drive to automate, simplify, and optimize carries over from coding to real life. However, there is a more compelling reason.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Technological advances mean my laptop can handle tasks that once required a desktop. But the laptop screen is even smaller than my old monitor. The net connection can be spotty, especially via tethering or a smartphone WiFi hotspot. The CPU often underclocks to save battery, and moreover, I fritter away cycles on fripperies such as visual effects: I’m a Compiz plugin junkie. I therefore encounter the same problems I faced over a decade ago.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;A Voyage to Lilliput&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Beyond laptops, we have netbooks, tablets, and smartphones. The world is getting smaller. Being a geek means I’ll attempt text editing, gaming, programming, etc. even as devices keep shrinking, bringing new user interfaces challenges. My current extreme case is my Nexus One, with its 3.7 inch display and no keyboard.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Rather than work on the phone directly, I ssh to a more powerful computer via &lt;a href='http://code.google.com/p/connectbot/'&gt;ConnectBot&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;In the past &lt;a href='http://benlynn.blogspot.com/2010/07/timeless-beauty-of-shell-scripts_31.html'&gt;I praised Bash one-liners&lt;/a&gt;. Shelling in from a smartphone drove me to appreciate Bash one-letter aliases. I’ve grown accustomed to using them on all computers. Some favourites:&lt;/p&gt;&lt;/div&gt;&lt;div class='listingblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;alias c=cd&lt;br /&gt;alias ..="cd .."&lt;br /&gt;alias v=vim&lt;br /&gt;alias g=git&lt;br /&gt;alias l="ls -CF --color=auto"&lt;br /&gt;alias s=sudo&lt;br /&gt;alias k=colormake&lt;br /&gt;alias rm='echo mv to /tmp instead'&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;(The "rm" alias trains me to avoid this command. See "Accidents Will Happen" in &lt;a href="http://www.simson.net/ref/ugh.pdf"&gt;The UNIX-HATERS Handbook&lt;/a&gt;.)&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Editing is unpleasant but bearable. In these circumstances, I feel Vim’s single-letter commands work in its favour, as does its modal nature. Also double-tapping the terminal brings up an "Esc" button, which suits Vim beautifully.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I don’t intend to code from my phone often as it’s like eating with tweezers. Though for fun, I followed through &lt;a href='http://benlynn.blogspot.com/2010/09/j-and-i_09.html'&gt;a suggestion in my last post&lt;/a&gt; and developed a J one-liner to sum the primes less than 100 using only a ConnectBox session on my phone. Soon I had:&lt;/p&gt;&lt;/div&gt;&lt;div class='listingblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;   +/p:i.p:^:(_1)100&lt;br /&gt;1060&lt;br /&gt;   +/i.&amp;amp;.(p:^:_1)100    NB. Cooler version.&lt;br /&gt;1060&lt;br /&gt;   v=:[:+/i.&amp;amp;.(p:^:_1)  NB. Tacit verb definition.&lt;br /&gt;   v 100&lt;br /&gt;1060&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-635135255105363499?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/635135255105363499/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=635135255105363499' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/635135255105363499'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/635135255105363499'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2010/09/moore-treadmill.html' title='Moore&amp;#39;s Treadmill'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-1882499272456715868</id><published>2010-09-09T07:09:00.001-07:00</published><updated>2011-04-06T01:49:02.283-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>J and I</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;&lt;div id='preamble'&gt;&lt;div class='sectionbody'&gt;&lt;div class='paragraph'&gt;&lt;p&gt;For years, I’ve been meaning to investigate &lt;a href='http://en.wikipedia.org/wiki/APL_(programming_language)'&gt;the APL programming language&lt;/a&gt; family. Months ago, I finally leafed through a few introductions to the new and improved APL known as &lt;a href='http://en.wikipedia.org/wiki/J_(programming_language)'&gt;J&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I liked what I saw. I enjoy paring down C code, but J makes my best efforts look as verbose as a legal contract. Introductory J examples are already cryptic enough to induce watering in the untrained eye. In the right hands, a J program can be compressed so densely that it threatens to collapse into a black hole of inscrutability.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I’ve heard about &lt;a href='http://thomasscovell.com/bio/tattoo/'&gt;an obfuscated C tattoo&lt;/a&gt;, as well as &lt;a href='http://lemonodor.com/archives/2007/08/code_tattoo.html'&gt;a Lisp tattoo&lt;/a&gt;. They should have gotten J tattoos! Instead of Hello World or the Fibonacci sequence, why not sport a terse program that &lt;a href='http://www.jsoftware.com/jwiki/Essays/Sudoku'&gt;solves a Sudoku&lt;/a&gt;, or &lt;a href='http://www.jsoftware.com/jwiki/Essays/QR%20Decomposition'&gt;finds a QR decomposition of a matrix&lt;/a&gt;?&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;More generally, J appears to be ideal when source size matters most. For example, programming via a smartphone with a tiny screen and keyboard.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;A day at play with J&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;J systems are freely available, but I reasoned I’d get a better feel for the language by writing my own interpreter. Thanks to J’s elegant design, I soon got the celebrated "mean=:+/%#" example working. That was enough to gain an appreciation (but also a little contempt) for J’s simple grammar, array handling, and above all, compact notation. A J symbol is worth a thousand machine words. &lt;a href='http://www-cs-students.stanford.edu/~blynn/c/ch05.html'&gt;See my J notes&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I kept picking at it for a while, but I think I’ll stop soon. It’d take substantial effort to implement types apart from doubles, not to mention error handling, memory management, arrays with axes of zero length, and fills.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Git repository: &lt;a href='http://cs.stanford.edu/~blynn/scrap/j0.git'&gt;a primordial J interpreter&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-1882499272456715868?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/1882499272456715868/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=1882499272456715868' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1882499272456715868'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1882499272456715868'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2010/09/j-and-i_09.html' title='J and I'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-3854809823641383577</id><published>2010-07-31T22:19:00.002-07:00</published><updated>2011-04-06T01:49:02.288-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='puzzles'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>The timeless beauty of shell scripts</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;&lt;div id='preamble'&gt;&lt;div class='sectionbody'&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Long ago, Doug McIlroy wrote: “This is the Unix philosophy: Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface.”&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Years later, Rob Pike observed: “Those days are dead and gone and the eulogy was delivered by Perl.” His statement mostly stands, except Python is the new Perl.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I refuse to abandon the old ways. So when a friend pointed me to the intriguing &lt;a href='http://www.pythonchallenge.com/about.php'&gt;Python Challenge&lt;/a&gt;, I avoided Python as much as I could. Instead, I used the Bash shell to string together special-purpose tools.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;The FAQ states the purpose of the challenge is to “provide an entertaining way to explore the Python Programming Language”, and “demonstrate the great power of Python’s batteries”. However, I feel it is better suited for training shell script muscles: most of the problems are of the one-shot trivial variety that suit Unix tools.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;A full-featured language is often overkill. Abraham Maslow’s quote comes to mind: “It is tempting, if the only tool you have is a hammer, to treat everything as if it were a nail.” I don’t mean to disparage Python, but I feel shell scripts are often overlooked and under-appreciated, especially as they are so accessible. Technically, you’re already shell programming when you run a program from the command-line. Why not learn a bit of Bash or similar, and increase the power available at your fingertips?&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;Brevity is the soul of wit&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;While general-purpose scripting languages have their place, judging by the posted solutions, for typical riddles in the Python Challenge, a short Python script is often outdone by a shorter still Bash incantation. In fact, in the first challenge you can stay in your shell. Did you know Bash natively handles (fixed-width precision) arithmetic? For example:&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;$ echo $((2**42))&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Naturally, if arbitrary precision were needed, we could invoke a specialized tool:&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;$ echo 10^100 | bc&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Humble Unix tools yield the most succint solution for several challenges. For example, a Caesar shift is probably terser with &lt;strong&gt;tr&lt;/strong&gt; than any popular language:&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;$ tr a-z l-za-m&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Or extracting lowercase letters from a file:&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;$ tr -cd a-z&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;When regular expressions are involved, even though the code may look similar, the old guard such as awk, sed, and grep that feature regularly in Bash scripts have an inherent advantage over Python (and Perl, PHP, Ruby, …). &lt;a href='http://swtch.com/~rsc/regexp/regexp1.html'&gt;Python takes exponential time to match some regular expressions&lt;/a&gt; whereas the classic Unix tools take polynomial time to match the same expressions.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;On the downside, Bash makes some tasks tiresome. I can’t think of an easy way to convert an decimal number to an ASCII character. This &lt;a href='http://mywiki.wooledge.org/BashFAQ/071'&gt;Bash FAQ&lt;/a&gt; suggests the cumbersome:&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;$ for a in 66 101 110; do printf \\$(printf '%03o' $a); done; echo&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Another chore is repeating a character a given number of times. Other than a loop, perhaps the easiest hack is something like:&lt;/p&gt;&lt;/div&gt;&lt;div class='literalblock'&gt;&lt;div class='content'&gt;&lt;pre&gt;&lt;tt&gt;$ printf "%042d" 0 | tr 0 x&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;A tiny elegant Haskell solution exists for problem 14, thanks to the transpose function and the language’s concise notation for recursion and composition. A search revealed Bash fans often employ a simple but tedious Awk script for matrix transposition, suggesting a Bash solution is necessarily significantly longer.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Happily, these blemishes are dwarfed by the successes of the Unix philosophy. More than once, my script has been simpler and briefer than any other posted solution because the complexity is hidden within a tool that does one thing, and does it well. My proudest achievement is a one-liner to compute the look-and-say sequence [hint: uniq -c].&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-3854809823641383577?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/3854809823641383577/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=3854809823641383577' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3854809823641383577'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3854809823641383577'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2010/07/timeless-beauty-of-shell-scripts_31.html' title='The timeless beauty of shell scripts'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-354283338571307879</id><published>2010-07-11T22:45:00.001-07:00</published><updated>2011-04-06T01:49:02.295-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Chinese Input</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;div id="preamble"&gt;&lt;div class="sectionbody"&gt;&lt;div class="paragraph"&gt;&lt;a href="http://translate.google.com/"&gt;Google Translate&lt;/a&gt; supplies a clumsy but straightforward means for entering Chinese characters with a US keyboard, especially for those learning the language. Simply type the English meaning, and copy the result. You can check the character is indeed the one you want by clicking on "Show romanization", or on the speaker icon to hear a synthesized reading.&lt;/div&gt;&lt;div class="paragraph"&gt;However, sometimes I have a particular character in mind. A short-term solution is to use &lt;a href="http://zhongwen.com/zi.htm"&gt;an online rendition of a traditional dictionary ordered by radical and stroke count&lt;/a&gt;, or &lt;a href="http://www.mandarintools.com/worddict.html"&gt;a pinyin dictionary&lt;/a&gt;. Additional speed and convenience requires investment; one must learn one of the many fascinating &lt;a href="http://en.wikipedia.org/wiki/Chinese_input_methods_for_computers"&gt;methods for entering Chinese characters on a computer&lt;/a&gt;.&lt;/div&gt;&lt;div class="paragraph"&gt;I’ve read that the &lt;a href="http://en.wikipedia.org/wiki/Wubi_method"&gt;Wubizixing input method&lt;/a&gt; is fastest, though as one might expect, it requires the most investment. Proficiency demands much practice with a suitably annotated keyboard.&lt;/div&gt;&lt;div class="paragraph"&gt;For now, I’ve opted for the &lt;a href="http://en.wikipedia.org/wiki/Wubihua"&gt;Wubihua method&lt;/a&gt;, which mimics how humans write characters. It may be slower, but it can be learned quickly. Also it applies when sending Chinese text messages on mobile phones.&lt;/div&gt;&lt;div class="paragraph"&gt;I supplement Wubihua with a &lt;a href="http://en.wikipedia.org/wiki/Pinyin_method"&gt;pinyin method&lt;/a&gt;, as I’m practically illiterate in Chinese.&lt;/div&gt;&lt;div class="paragraph"&gt;&lt;strong&gt;Chinese input in Linux&lt;/strong&gt;&lt;/div&gt;&lt;div class="paragraph"&gt;To setup Chinese input methods in Ubuntu, I installed the &lt;tt&gt;scim&lt;/tt&gt; and &lt;tt&gt;scim-pinyin&lt;/tt&gt; packages, then modified my &lt;tt&gt;.xsession&lt;/tt&gt; as follows. I prepended:&lt;/div&gt;&lt;div class="literalblock"&gt;&lt;div class="content"&gt;&lt;pre&gt;&lt;tt&gt;export XMODIFIERS="@im=SCIM"&lt;br /&gt;export GTK_IM_MODULE="xim"&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="paragraph"&gt;and appended:&lt;/div&gt;&lt;div class="literalblock"&gt;&lt;div class="content"&gt;&lt;pre&gt;&lt;tt&gt;scim -d&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="paragraph"&gt;after which pressing Ctrl+Space toggles Chinese input.&lt;/div&gt;&lt;div class="paragraph"&gt;"Stroke 5" is Wubihua mode. Stroke types are mapped to 5 keys along the bottom row. From right to left:&lt;/div&gt;&lt;div class="paragraph"&gt;&lt;tt&gt;/&lt;/tt&gt;: | (vertical; top-to-bottom)&lt;/div&gt;&lt;div class="paragraph"&gt;&lt;tt&gt;.&lt;/tt&gt;: \ (downwards left-to-right)&lt;/div&gt;&lt;div class="paragraph"&gt;&lt;tt&gt;,&lt;/tt&gt;: / (downwards right-to-left)&lt;/div&gt;&lt;div class="paragraph"&gt;&lt;tt&gt;m&lt;/tt&gt;: - (horizontal; left-to-right)&lt;/div&gt;&lt;div class="paragraph"&gt;&lt;tt&gt;n&lt;/tt&gt;: other stroke types, e.g: 乙&lt;/div&gt;&lt;div class="paragraph"&gt;Perhaps one can remember this as follows: 乙 looks like a rotated N. A lowercase M takes more horizontal space than most letters, so it corresponds to the horizontal stroke. On the next two keys, the less-than and greater-than signs point left and right, so they correspond to the left and right downward strokes. Lastly, the stem of the question mark suggests a vertical stroke.&lt;/div&gt;&lt;div class="paragraph"&gt;Although the pinyin method is found in the Simplified menu (智能拼音; "smart pinyin"), it also offers Traditional characters. I originally learned zhuyin (aka bopomofo), a more traditional pronounciation-based method for producing Traditional characters, but it is ill-suited for a US keyboard. Fortunately &lt;a href="http://www.yellowbridge.com/chinese/zhuyin.php"&gt;converting from zhuyin to pinyin&lt;/a&gt; is trivial.&lt;/div&gt;&lt;div class="paragraph"&gt;&lt;strong&gt;Chinese to English&lt;/strong&gt;&lt;/div&gt;&lt;div class="paragraph"&gt;In a pinch, I’ll use Google Translate to learn Chinese phrases. However, browser plugins are much handier; see the &lt;a href="https://chrome.google.com/extensions/detail/kkmlkkjojmombglmlpbpapmhcaljjkde"&gt;Chrome Zhongwen extension&lt;/a&gt; and the &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/3349/"&gt;Firefox Perapera-kun add-on&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-354283338571307879?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/354283338571307879/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=354283338571307879' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/354283338571307879'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/354283338571307879'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2010/07/chinese-input_11.html' title='Chinese Input'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-6668152220866916925</id><published>2010-05-22T09:14:00.002-07:00</published><updated>2011-04-06T01:49:02.298-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Stack-smashing remains fun and profitable</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;&lt;div id='preamble'&gt;&lt;div class='sectionbody'&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;a href='http://insecure.org/stf/smashstack.html'&gt;Buffer overflows&lt;/a&gt; have long been a rich source of security vulnerabilities. Unfortunately, its popularity led to a family of knee-jerk reactions: &lt;a href='http://en.wikipedia.org/wiki/W^X'&gt;W^X&lt;/a&gt;; Data Execution Prevention (DEP); the NX (No eXecute) bit; executable space protection.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;As the terms suggest, code is divorced from data. For example, an operating system may forbid execution of any instructions lying in the stack. A simple buffer exploit now causes the program to halt, rather than execute injected code.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;My knee-jerk counter-reaction was suspicion and hostility. Shouldn’t we be focusing on the causes of buffer overflow disease, and not its symptoms? Also, some of the most beautiful and fundamental results in computer science involve feeding a Turing machine to a Turing machine. Data is code and code is data.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;Self-modifying code considered awesome&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Self-modifying code is fascinating by virtue of its self-referential nature, but it is not just a pretty face: it has practical applications. Just-in-time compilation is perhaps the best-known example. However, I find it most useful for nested functions in C.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;a href='http://web.pdx.edu/~hegbloom/download/Usenix88-lexic.pdf'&gt;Thomas Breuel describes how a C compiler can be modified to allow nested function via trampolining&lt;/a&gt;. Briefly, for each nested function, we generate its code as usual, but we also add a local variable to the scope where it is defined: we add an array of bytes, whose contents are opcodes that simply rig a pointer to make it look like we’re in the current stack frame before jumping to the nested function.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Standard C code expecting a function pointer still works when passed a pointer to this array. When they call the function pointer, they jump to the array, where they run the stack-frame-rigging code before continuing on with the call. By magic, the code in the nested function executes within the desired scope. One pointer does the work of two.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Observe we require the array to be local and populated at runtime, because only then do we know the address of the current stack frame. We cannot setup this tomfoolery in advance. In other words, we must execute code we just placed on the stack.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Like duct tape, treating data as code deftly solves a host of problems. W^X and friends block this avenue, or at least make it less efficient. Breuel writes:&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;"There are, however, some architectures and/or operating systems that forbid a program to generate and execute code at runtime. We consider this restriction arbitrary and consider it poor hardware or software design. Implementations of programming languages such as FORTH, Lisp, or Smalltalk can benefit significantly from the ability to generate or modify code quickly at runtime."&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;Return-oriented programming&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;a href='http://cseweb.ucsd.edu/~hovav/'&gt;Hovav Shacham&lt;/a&gt; recently filled me in on &lt;a href='http://en.wikipedia.org/wiki/Return-oriented_programming'&gt;return-oriented programming&lt;/a&gt;. I’m a fan. I now have more than gut feelings to support my stance. I can gloat and say "I told you so".&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Return-oriented programming skirts around stupid restrictions via one level of indirection. Instead of putting code in the stack, we put pointers to the code in the stack. We choose to point at code that soon runs into a return instruction. On typical architectures, a return instruction increments the stack pointer before jumping to the address to which it points. Hence the stack pointer becomes a sort of indirect instruction pointer: we run a snippet of code until it hits a return statement, which causes us to run the next snippet of code, and so on.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Thus using a compromised stack, attackers cleverly glue together snippets of code in executable parts of memory, such as the BIOS or the standard C library. On popular systems there’s enough stuff to do anything. Tools for automating the process exist; namely, you can write source and have it compile to a sequence of cherry-picked memory addresses. It’s &lt;a href='http://en.wikipedia.org/wiki/Return-to-libc_attack'&gt;the return-to-libc attack&lt;/a&gt; on steroids.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I had hoped return-oriented programming could cut both ways; does it allow self-modifying code even in the presence of an NX bit? Sadly, on further reflection, return-oriented programming appears to have no legitimate uses. Arbitrary code execution is only possbile within the stack frame of a function we control, and, for example, we still have no means of adjusting the static link pointer when qsort() invokes our nested function. Hopefully I overlooked a sneaky trick.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;In short, thanks to return-oriented programming, executable space protection is a minor inconvenience for the bad guys, and a major inconvenience for the good guys.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-6668152220866916925?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/6668152220866916925/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=6668152220866916925' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/6668152220866916925'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/6668152220866916925'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2010/05/stack-smashing-remains-fun-and.html' title='Stack-smashing remains fun and profitable'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-5262289402278814027</id><published>2010-04-10T11:06:00.001-07:00</published><updated>2011-04-06T01:49:02.304-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='shamelessplug'/><title type='text'>Self-publishing with CreateSpace</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;&lt;div id='preamble'&gt;&lt;div class='sectionbody'&gt;&lt;div class='paragraph'&gt;&lt;p&gt;From time to time, somebody sends me a kind email saying that they only truly appreciated Git after encountering &lt;a href='http://cs.stanford.edu/~blynn/gitmagic'&gt;my Git guide&lt;/a&gt;. One such reader had already bought a few Git books, and he suggested I should therefore turn my website into a book.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I had idly thought about doing this, but why bother? Was: FREE, Now: $9.95!? However, the email made me realize that some seek information by buying books first, then look around online if they want more. Making a book out of my guide might be a good idea after all: I’m not trying to sell it to people who already know they can read it for free; rather, I’m aiming for those who might not otherwise find it until much later because they visit bookshops before search engines.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;CreateSpace&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Because the most renowned technical publishers already offered books on Git, I chose to self-publish on &lt;a href='http://www.createspace.com/'&gt;CreateSpace&lt;/a&gt;. Their tools are free, and they list your work on &lt;a href='http://www.amazon.com/'&gt;Amazon&lt;/a&gt; (who owns CreateSpace). I’d love to have bricks-and-mortar bookshops carry copies of the book too, but an Amazon listing should be enough for now.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;In a brief search, I found controversy over CreateSpace ISBNs, but &lt;a href='http://www.publetariat.com/publish/truth-about-createspaces-free-isbns'&gt;Richard Sutton’s post&lt;/a&gt; reassured me: firstly, for my book, the issues stemming from CreateSpace being the registered owner of the ISBN are irrelevant, and secondly, if you really want you can have an ISBN registered in your name (but you’ll have to buy it yourself).&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;The whole process is not quite free. After submitting your PDF file, you must order a proof copy. If you find errors, you submit a corrected PDF, and repeat. I made a stupid mistake the first time, so I went through this cycle twice and finished down about 16 bucks.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;It’s not all bad though. I was surprisingly pleased to hold my book in my hand, as it felt like I had accomplished something. Also, in print form, the same old sentences become more authoritative and strangely convincing. Online, they look like stuff that some guy posted on some random website.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Preparing the book took much longer than expected. I had mentioned to a reader that I was considering making a book. I tried follow advice he gave me so it would look less amateurish. I cut a chapter and an appendix. I added an index. I renamed headings so they were more descriptive. I replaced all variables (e.g. "SHA1_HASH") in the command-line examples with values (e.g. "1b6d"). I selected a 6 inch by 9 inch form factor, which meant I had to shorten some lines to get them to fit. While doing all this, I found poorly spelled words, poorly worded paragraphs and poorly organized sections. I doubt I caught them all.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;To avoid further delays, I used their Easy Cover Creator. Perhaps I’ll revisit this eventually, as I want a more spartan look: something like &lt;a href='http://en.wikipedia.org/wiki/The_C_Programming_Language_(book)'&gt;Kernighan and Ritchie’s "The C programming language"&lt;/a&gt;. Or perhaps a sort of cheat sheet so the book would be useful even while shut.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I set the price to $9.95 USD, which means I get 2 bucks or so per sale. I considered a lower price, but I’ll be lucky to make my $16 back as it is! Still, it ought to be low enough that a buyer won’t be too annoyed when they find out the material is freely available on my homepage. (I would have linked to the free version from the book description, but this is forbidden.)&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;AsciiDoc, xsltproc, fop&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I had some trouble with my tool chain that produces PDFs from text files. &lt;a href='http://www.methods.co.nz/asciidoc/'&gt;AsciiDoc&lt;/a&gt; produces a &lt;a href='http://www.docbook.org/'&gt;DocBook&lt;/a&gt; XML file out of the source text, which &lt;a href='http://xmlsoft.org/XSLT/xsltproc2.html'&gt;xsltproc&lt;/a&gt; turns into an &lt;a href='http://en.wikipedia.org/wiki/XSL_Formatting_Objects'&gt;XSL-FO&lt;/a&gt; file, which &lt;a href='http://xmlgraphics.apache.org/fop/'&gt;fop&lt;/a&gt; renders into a PDF. The design of the various formats probably have technical merit, but I found it difficult to figure out how to get what I wanted.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;For example, I replaced variables with values because I could not italicize them easily with AsciiDoc. The only methods I discovered destroyed the natural beauty of the source text.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;It seems the smaller the detail, the larger the effort required to tune it. Changing page sizes, font sizes and chapter heading styles was easy enough to figure out, but I still don’t know the right way to insert a blank page after the front matter so the first chapter starts on an odd page. I gave up editing some XSL file or other. Instead, I scripted a fragile search-and-replace on the XSL-FO output.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Nonetheless, I stand by my choices. There’s something appealing about source files which resemble old-school text files. Also, once the configuration nightmare is over, editing is simple: I can use any text editor, and the tool chain will automatically produce several HTML versions as well as a reasonable PDF for a book.&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;Shameless plug&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;I couldn’t possibly end this post without a link to my book: "&lt;a href='http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/'&gt;Git Magic&lt;/a&gt;". It’s the most important book you’ll ever have, or my name is not Winston! &lt;a href='http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/'&gt;Buy it now&lt;/a&gt;!&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-5262289402278814027?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/5262289402278814027/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=5262289402278814027' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5262289402278814027'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5262289402278814027'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2010/04/self-publishing-with-createspace_10.html' title='Self-publishing with CreateSpace'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-2122668272600516145</id><published>2010-04-07T00:03:00.002-07:00</published><updated>2011-04-06T01:49:02.311-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Nginx and FastCGI</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt; &lt;div id='preamble'&gt; &lt;div class='sectionbody'&gt; &lt;div class='paragraph'&gt;&lt;p&gt;One perk I miss from my first days of grad school was my office computer with a permanent IP address. I could run all sorts of servers. (Later, the environment became harsher because unlike me, many of us ran Windows, but like me, they did not know how to do so securely. The IT team restricted most ports as the first line of defence, though you could ask for exceptions. Hopefully they didn’t tighten control further after I graduated.)&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Wanting to be cool, I experimented with &lt;a href='http://php.net/'&gt;PHP&lt;/a&gt; when it started becoming popular. Until then, I had only dabbled with CGI programs in compiled languages. PHP was intoxicating. A Common Gateway drug, so to speak. In those Web 1.0 days, dynamic webpages were so easy and fun to make with PHP that I overdosed.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Years later I finally admitted to myself that my content was static apart from a few needless gimmicks, and this was unlikely to change. Using PHP was only increasing the CPU load. It didn’t matter because I received few hits, but it offended me as a computer scientist. I sobered up and returned to vanilla HTML.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;I’ve been thinking how I might write a web application today, and I realized I’ve come full circle. I’ve lost my taste for &lt;a href='http://en.wikipedia.org/wiki/LAMP_(software_bundle)'&gt;LAMP stacks&lt;/a&gt; at a time when they are more widespread than ever, and once again espouse compiled languages.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;Nginx&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Firstly, I’ve moved on from Apache, which was once my favourite web server. I used to watch &lt;a href='http://news.netcraft.com/'&gt;Netcraft&lt;/a&gt;'s market share graphs so I could cheer on Apache against commercial products. But one day, I noticed a newcomer on the graphs. A strange jumble of letters: "nginx". I couldn’t resist looking it up.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;&lt;a href='http://nginx.org/'&gt;Nginx&lt;/a&gt; shows how powerful pure unadulterated C can be in the right hands. Written by Igor Sysoev, this webserver runs on numerous platforms, hardly using any memory even as it handles thousands of requests at light speed. Nginx has somehow dodged &lt;a href='http://pl.atyp.us/content/tech/servers.html'&gt;Jeff Darcy’s Four Horsemen of Poor Performance&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Nginx cannot do CGI, but it can do &lt;a href='http://www.fastcgi.com/drupal/'&gt;FastCGI&lt;/a&gt;, which is a plus. Instead of spawning a new process for every request, FastCGI spawns a long-lived program once, which communicates with the webserver when necessary, possibly over a network. Of course, this program can run threads of its own if desired.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;An advantage of LAMP stacks was that scripts could run without requiring a new process or thread. FastCGI puts all languages on the same footing. In fact, FastCGI is more flexible: for example, you can restart FastCGI programs independently of webservers. Perhaps this is why some run PHP via FastCGI.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;Compiled languages&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;I prefer a compiled language to a scripting language like PHP because I crave speed and scalability. Also, one feature of PHP is useless to me: I discovered I lack the discipline to mix code with HTML. At first I found it was convenient, but eventually my webpages became hard to maintain. I now insist on strict separation between languages: my CSS, JavaScript, HTML, and whatever else ideally reside in distinct files.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Also, now that JavaScript is ubiquitous, it seems best to push as much work as possible to the client side: the FastCGI should do the minimum possible and supply its results (perhaps in &lt;a href='http://en.wikipedia.org/wiki/JSON'&gt;JSON&lt;/a&gt;) to JavaScript which then plays with the data using the client’s CPU. This diminishes the need for a language designed to mingle with HTML.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Running a web application with a scripting language purportedly allows rapid prototyping, but it seems the only drawback to a compiled language is a compilation step and a FastCGI program restart. This is negligible provided your language has a fast compiler (like C and Go). Besides, I bet much of the development cycle involves presentation tweaks, that is, edits to CSS, HTML, and JavaScript: not the compiled language.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;The L and M of LAMP&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;I’d still run my servers on Linux. I’ve had good results with it so far. As for MySQL, I cannot say, having never experimented much with databases. Its reputation seems solid enough.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;How-to&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;On the latest Ubuntu, you’ll need to install the packages nginx, spawn-fcgi, libfcgi-dev. Then edit the nginx configuration file in &lt;tt&gt;/etc/nginx/sites-available/default&lt;/tt&gt;. In the &lt;tt&gt;server&lt;/tt&gt; clause, add something like:&lt;/p&gt;&lt;/div&gt; &lt;div class='literalblock'&gt; &lt;div class='content'&gt; &lt;pre&gt;&lt;tt&gt;location = /test {&lt;br/&gt;  fastcgi_pass 127.0.0.1:9000;&lt;br/&gt;  fastcgi_param QUERY_STRING $query_string;&lt;br/&gt;}&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;The file &lt;tt&gt;/etc/nginx/fastcgi_params&lt;/tt&gt; contains other parameters you might want to pass. Restart nginx, by running for example:&lt;/p&gt;&lt;/div&gt; &lt;div class='literalblock'&gt; &lt;div class='content'&gt; &lt;pre&gt;&lt;tt&gt;$ sudo /etc/init.d/nginx restart&lt;/tt&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Visiting &lt;a href='http://localhost/test'&gt;http://localhost/test&lt;/a&gt; should result in a 502 error because no FastCGI program is running yet.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Let’s fix this. In C, I recommend using &lt;tt&gt;fcgiapp.h&lt;/tt&gt; and not &lt;tt&gt;fcgi_stdio.h&lt;/tt&gt;; it’s not much more trouble, and you avoid conflicts with the standard &lt;tt&gt;stdio&lt;/tt&gt; library.&lt;/p&gt;&lt;/div&gt; &lt;div class='listingblock'&gt; &lt;div class='content'&gt; &lt;pre&gt;&lt;tt&gt;&lt;span style='font-weight: bold'&gt;&lt;span style='color: #000080'&gt;#include&lt;/span&gt;&lt;/span&gt; &lt;span style='color: #FF0000'&gt;&amp;lt;fcgiapp.h&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;&lt;span style='color: #009900'&gt;int&lt;/span&gt; &lt;span style='font-weight: bold'&gt;&lt;span style='color: #000000'&gt;main&lt;/span&gt;&lt;/span&gt;&lt;span style='color: #990000'&gt;()&lt;/span&gt; &lt;span style='color: #FF0000'&gt;{&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #008080'&gt;FCGX_Stream&lt;/span&gt; &lt;span style='color: #990000'&gt;*&lt;/span&gt;in&lt;span style='color: #990000'&gt;,&lt;/span&gt; &lt;span style='color: #990000'&gt;*&lt;/span&gt;out&lt;span style='color: #990000'&gt;,&lt;/span&gt; &lt;span style='color: #990000'&gt;*&lt;/span&gt;err&lt;span style='color: #990000'&gt;;&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #008080'&gt;FCGX_ParamArray&lt;/span&gt; envp&lt;span style='color: #990000'&gt;;&lt;/span&gt;&lt;br/&gt;  &lt;span style='font-weight: bold'&gt;&lt;span style='color: #0000FF'&gt;while&lt;/span&gt;&lt;/span&gt; &lt;span style='color: #990000'&gt;(&lt;/span&gt;&lt;span style='font-weight: bold'&gt;&lt;span style='color: #000000'&gt;FCGX_Accept&lt;/span&gt;&lt;/span&gt;&lt;span style='color: #990000'&gt;(&amp;amp;&lt;/span&gt;in&lt;span style='color: #990000'&gt;,&lt;/span&gt; &lt;span style='color: #990000'&gt;&amp;amp;&lt;/span&gt;out&lt;span style='color: #990000'&gt;,&lt;/span&gt; &lt;span style='color: #990000'&gt;&amp;amp;&lt;/span&gt;err&lt;span style='color: #990000'&gt;,&lt;/span&gt; &lt;span style='color: #990000'&gt;&amp;amp;&lt;/span&gt;envp&lt;span style='color: #990000'&gt;)&lt;/span&gt; &lt;span style='color: #990000'&gt;&amp;gt;=&lt;/span&gt; &lt;span style='color: #993399'&gt;0&lt;/span&gt;&lt;span style='color: #990000'&gt;)&lt;/span&gt; &lt;span style='color: #FF0000'&gt;{&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #009900'&gt;char&lt;/span&gt; &lt;span style='color: #990000'&gt;*&lt;/span&gt;q &lt;span style='color: #990000'&gt;=&lt;/span&gt; &lt;span style='font-weight: bold'&gt;&lt;span style='color: #000000'&gt;FCGX_GetParam&lt;/span&gt;&lt;/span&gt;&lt;span style='color: #990000'&gt;(&lt;/span&gt;&lt;span style='color: #FF0000'&gt;"QUERY_STRING"&lt;/span&gt;&lt;span style='color: #990000'&gt;,&lt;/span&gt; envp&lt;span style='color: #990000'&gt;);&lt;/span&gt;&lt;br/&gt;    &lt;span style='font-weight: bold'&gt;&lt;span style='color: #000000'&gt;FCGX_FPrintF&lt;/span&gt;&lt;/span&gt;&lt;span style='color: #990000'&gt;(&lt;/span&gt;out&lt;span style='color: #990000'&gt;,&lt;/span&gt; &lt;span style='color: #FF0000'&gt;"Content-type: text/plain&lt;/span&gt;&lt;span style='color: #CC33CC'&gt;\r\n\r\n&lt;/span&gt;&lt;span style='color: #FF0000'&gt;"&lt;/span&gt;&lt;span style='color: #990000'&gt;);&lt;/span&gt;&lt;br/&gt;    &lt;span style='font-weight: bold'&gt;&lt;span style='color: #0000FF'&gt;if&lt;/span&gt;&lt;/span&gt; &lt;span style='color: #990000'&gt;(!&lt;/span&gt;q&lt;span style='color: #990000'&gt;)&lt;/span&gt; &lt;span style='color: #FF0000'&gt;{&lt;/span&gt;&lt;br/&gt;      &lt;span style='font-weight: bold'&gt;&lt;span style='color: #000000'&gt;FCGX_FPrintF&lt;/span&gt;&lt;/span&gt;&lt;span style='color: #990000'&gt;(&lt;/span&gt;out&lt;span style='color: #990000'&gt;,&lt;/span&gt;&lt;br/&gt;          &lt;span style='color: #FF0000'&gt;"no query string: check web server configuration&lt;/span&gt;&lt;span style='color: #CC33CC'&gt;\n&lt;/span&gt;&lt;span style='color: #FF0000'&gt;"&lt;/span&gt;&lt;span style='color: #990000'&gt;);&lt;/span&gt;&lt;br/&gt;    &lt;span style='color: #FF0000'&gt;}&lt;/span&gt;&lt;br/&gt;    &lt;span style='font-weight: bold'&gt;&lt;span style='color: #000000'&gt;FCGX_FPrintF&lt;/span&gt;&lt;/span&gt;&lt;span style='color: #990000'&gt;(&lt;/span&gt;out&lt;span style='color: #990000'&gt;,&lt;/span&gt; &lt;span style='color: #FF0000'&gt;"Query: '%s'&lt;/span&gt;&lt;span style='color: #CC33CC'&gt;\n&lt;/span&gt;&lt;span style='color: #FF0000'&gt;"&lt;/span&gt;&lt;span style='color: #990000'&gt;,&lt;/span&gt; q&lt;span style='color: #990000'&gt;);&lt;/span&gt;&lt;br/&gt;  &lt;span style='color: #FF0000'&gt;}&lt;/span&gt;&lt;br/&gt;  &lt;span style='font-weight: bold'&gt;&lt;span style='color: #0000FF'&gt;return&lt;/span&gt;&lt;/span&gt; &lt;span style='color: #993399'&gt;0&lt;/span&gt;&lt;span style='color: #990000'&gt;;&lt;/span&gt;&lt;br/&gt;&lt;span style='color: #FF0000'&gt;}&lt;/span&gt;&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class='paragraph'&gt;&lt;p&gt;Compile your code:&lt;/p&gt;&lt;/div&gt; &lt;div class='literalblock'&gt; &lt;div class='content'&gt; &lt;pre&gt;&lt;tt&gt;$ gcc a.c -lfcgi&lt;/tt&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Then spawn the binary on your machine on port 9000:&lt;/p&gt;&lt;/div&gt; &lt;div class='literalblock'&gt; &lt;div class='content'&gt; &lt;pre&gt;&lt;tt&gt;$ spawn-fcgi -a 127.0.0.1 -p 9000 -n -- a.out&lt;/tt&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Test it by visiting &lt;a href='http://localhost/test?example'&gt;http://localhost/test?example&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;In a real application, you might want to run the binary as a daemon, and place the process ID in a temporary file for easy access:&lt;/p&gt;&lt;/div&gt; &lt;div class='literalblock'&gt; &lt;div class='content'&gt; &lt;pre&gt;&lt;tt&gt;$ spawn-fcgi -a 127.0.0.1 -p 9000 -P /tmp/pid -- a.out&lt;/tt&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;I had planned to continue this post by writing about embedding HTML files in C, and fetching data with JavaScript but it’s too long as it is. Some other time maybe.&lt;/p&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt;      &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-2122668272600516145?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/2122668272600516145/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=2122668272600516145' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2122668272600516145'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2122668272600516145'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2010/04/nginx-and-fastcgi.html' title='Nginx and FastCGI'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-1607862638358330258</id><published>2010-04-05T18:48:00.002-07:00</published><updated>2011-04-06T01:49:02.315-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='games'/><title type='text'>At last</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_8FH64FXxxnM/S7qTL4bYgkI/AAAAAAAAYvE/plDUrVfeExk/s1600/max300c.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 300px;" src="http://4.bp.blogspot.com/_8FH64FXxxnM/S7qTL4bYgkI/AAAAAAAAYvE/plDUrVfeExk/s400/max300c.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5456835730847859266" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;I don't play much &lt;a href="http://en.wikipedia.org/wiki/Dance_Dance_Revolution"&gt;DDR&lt;/a&gt; anymore. Making it through Max 300 on Heavy mode was an old goal I thought I had no hope of achieving. But several days ago I had energy to burn and played a few rounds on a whim. Oddly, my game has improved despite lack of practice. The arrows felt slower than I remember. I tried Max 300 almost as a joke, and was amazed that I finally passed it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now I have to work my way up to an A!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-1607862638358330258?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/1607862638358330258/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=1607862638358330258' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1607862638358330258'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1607862638358330258'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2010/04/at-last_05.html' title='At last'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_8FH64FXxxnM/S7qTL4bYgkI/AAAAAAAAYvE/plDUrVfeExk/s72-c/max300c.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-2800922474995624864</id><published>2009-11-12T01:54:00.002-08:00</published><updated>2011-04-06T01:49:02.319-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>It's Go time</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt; &lt;div id='preamble'&gt; &lt;div class='sectionbody'&gt; &lt;div class='paragraph'&gt;&lt;p&gt;At last, &lt;a href='http://golang.org'&gt;the Go programming language&lt;/a&gt; has been publicly released so I can write about it without getting into big trouble.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Go was designed by programmers I hold in high regard. Although it is a new language, the same people already experimented with some of its ideas at least ten years ago.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Back then, I saw a demonstration of &lt;a href='http://www.vitanuova.com/inferno/'&gt;the Inferno operating system&lt;/a&gt;, the successor to &lt;a href='http://plan9.bell-labs.com/plan9/'&gt;Plan 9&lt;/a&gt;, which in turn was the sequel to UNIX. I remain awed that just a handful of programmers could implement a complex system so well. The venerable UNIX crew seem to make the right decision often, and make it ten years ahead of everyone else.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;For example, &lt;a href='http://www.vitanuova.com/inferno/papers/dis.html'&gt;the dis byte code&lt;/a&gt; ran on a register-based virtual machine and hence ran fast. In contrast, its chief competitor, Java, has a stack-based virtual machine, and it seemed to take years to realize that this was a poor solution. The problem is so severe that JIT compilation was introduced, weakening the "Write Once, Run Anywhere" mantra: the statement becomes almost meaningless if one requires some sort of compiler for every platform, as opposed to a simple byte code interpreter. Another workaround is to design &lt;a href='http://en.wikipedia.org/wiki/Dalvik_virtual_machine'&gt;a register-based virtual machine for Java&lt;/a&gt; from scratch.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Another case in point is &lt;a href='http://www.vitanuova.com/inferno/limbo.html'&gt;Limbo&lt;/a&gt;, a novel programming language that accompanied Inferno. Sadly Limbo has far fewer adherents than Java, for non-engineering reasons. Like Plan 9 (which remained closed-source until 2000), Inferno was hard to obtain, whereas Java was being given away: indeed, users were once practically forced by their browsers to install Java virtual machines. The relative obscurity of Limbo is therefore expected, as with any human language possessing few speakers and few opportunities for growth.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;&lt;strong&gt;A second chance&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;It’s much more exciting this time around. The powers that be have wisely made Go open source. Now that Go is playing on a level field, ideas that Limbo should have popularized finally have a chance to spread far and wide.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;My favourite feature is something they left out. As with Limbo, there is no inheritance. There is no stifling type system. At least one generation of programmers has been trained to use a rigid type system, and I believe history will one day prove the inheritance mindset to be a passing fad. I have strong feelings about this topic because I was once a firm believer in inheritance, and it took years to realize my mistake.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Go instead emphasizes interfaces, and provides a simple concise syntax for them. A few neat lines perform the equivalent of several messy lines in C that deal with structs of function pointers.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Go also has strong concurrent programming features, which it shares with Limbo. Different threads (actually, "goroutines") communicate via channels (folowing the CSP model), eliminating race conditions. Channels are essentially type-safe UNIX pipes: they capture the power and delight of writing shell scripts to string together several tools.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Among lesser niceties: nested functions, anonymous functions, multiple return values, the package and import keywords, untyped numerical constants, reflection. Most of &lt;a href='http://crypto.stanford.edu/~blynn/c/ch08.html'&gt;my wishes for C&lt;/a&gt; have been granted.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;I’ll probably mostly stick with C. I’ve grown accustomed to its flaws, and I like squeezing every drop of performance out of code without dropping down to assembly. But for those tasks that require more than a shell script but less than a C project, Go might just fit the bill.&lt;/p&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt;      &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-2800922474995624864?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/2800922474995624864/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=2800922474995624864' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2800922474995624864'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2800922474995624864'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2009/11/it-go-time.html' title='It&amp;#39;s Go time'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-8351485639610424861</id><published>2009-11-08T23:57:00.002-08:00</published><updated>2011-04-06T01:49:02.324-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='games'/><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><category scheme='http://www.blogger.com/atom/ns#' term='puzzles'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Project Euler</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt; &lt;div id='preamble'&gt; &lt;div class='sectionbody'&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Lately my spare time has been eaten by an MMORPG: &lt;a href='http://projecteuler.net/'&gt;Project Euler&lt;/a&gt;. Even though I have played few RPGs, I’m sure Project Euler is one of the most difficult and challenging.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Actually, it’s really not an RPG. Rather than reward the clicking of buttons, the only way to gain levels in Project Euler is to submit answers to puzzles. Each involves a little bit of computer science and a little bit of mathematics.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Along the way, I wrote a library to make it easier to do arbitrary precision arithmetic in C. For example, to solve problem 97, I could write:&lt;/p&gt;&lt;/div&gt; &lt;div class='listingblock'&gt; &lt;div class='content'&gt; &lt;pre&gt;&lt;tt&gt;  mpz_t z;&lt;br/&gt;  mpz_init(z);&lt;br/&gt;  mpx_eval("mpz a; a = (28433 * 2^7830457 + 1) % 10^10;", z);&lt;br/&gt;  gmp_printf("%Zd\n", z);&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Strangely, Project Euler only lets you try at most 25 problems over &lt;em&gt;n&lt;/em&gt; days, where &lt;em&gt;n&lt;/em&gt; is your current level (except at level 0, when it is 1). Perhaps I should be grateful, as bumping into this limit gives me time to post code!&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;mpx git repository: &lt;a href='http://cs.stanford.edu/~blynn/scrap/mpx.git'&gt;http://cs.stanford.edu/~blynn/scrap/mpx.git&lt;/a&gt;&lt;/p&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt;      &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-8351485639610424861?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/8351485639610424861/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=8351485639610424861' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/8351485639610424861'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/8351485639610424861'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2009/11/project-euler.html' title='Project Euler'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-3598813440877985380</id><published>2009-11-02T00:40:00.002-08:00</published><updated>2011-04-06T01:49:02.328-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>I'm right, you're wrong</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt; &lt;div id='preamble'&gt; &lt;div class='sectionbody'&gt; &lt;div class='paragraph'&gt;&lt;p&gt;I enjoy ranting about programming languages, whatever the medium: blog posts, emails to friends, colleagues and strangers, verbally to anyone within earshot. Mostly I rail against the evils of object-oriented languages, and why C is still the one true language.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Over the years, I’ve amassed &lt;a href='http://cs.stanford.edu/~blynn/c/'&gt;enough material to fill several pages, which I’m now launching&lt;/a&gt;. Hopefully if I concentrate all these tirades in one place I’ll expend less energy overall on arguing!&lt;/p&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt;      &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-3598813440877985380?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/3598813440877985380/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=3598813440877985380' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3598813440877985380'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3598813440877985380'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2009/11/i-right-you-wrong_02.html' title='I&amp;#39;m right, you&amp;#39;re wrong'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-7501174394949732972</id><published>2009-08-24T15:14:00.002-07:00</published><updated>2011-04-06T01:49:02.330-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Napkin Cryptography</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt; &lt;div id="preamble"&gt; &lt;div class="sectionbody"&gt; &lt;div class="paragraph"&gt;&lt;p&gt;I was a cryptography researcher in a past life. Occasionally I experience echoes from my previous incarnation: a paper review here, a technical question there, so I still feel somewhat connected.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;A couple of weeks ago I bumped into &lt;a href="http://en.wikipedia.org/wiki/Daniel_J._Bernstein"&gt;Daniel J. Bernstein&lt;/a&gt; and &lt;a href="http://www.hyperelliptic.org/tanja/"&gt;Tanja Lange&lt;/a&gt;, whom I had last seen in 2003 at a conference in Chicago. [Thanks to Dan for inviting me, and also for taking everyone out to the downtown bars and restaurants. I had a blast!] Only then did I realize how far I had fallen from the light. In a hurried notes-on-a-napkin conversation I tried to catch up on what was once my field:&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;&lt;strong&gt;2048 is the new 1024&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Firstly, I was mildly surprised when I learned governments and standards bodies are actually heeding warnings that 1024-bit RSA keys are crackable by large corporations and botnets, thus now mandate 2048-bit keys.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Ah, the memories. Just how vulnerable are 1024-bit keys? Back in the ivory tower, this was the subject of an intense high-profile debate, with Bernstein on one side, and Lenstra, Shamir, Tomlinson and Tromer on the other. The heated arguments seemed to get personal at times, providing excellent fodder for gossip amongst us grad students.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;I have no idea if the parties ever reconciled, nor if the fireworks have even subsided, but I’m relieved that everybody agrees 1024 bits is insufficient nowadays.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;&lt;strong&gt;Fast ECC&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;More interesting to me are the new breed of elliptic curve cryptography (ECC) implementations. Dan instructed me to "forget everything you know about elliptic curves" before launching into the world’s fastest course on the world’s fastest algorithms for elliptic curves.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Consider a unit circle, &lt;em&gt;x&lt;/em&gt;&lt;sup&gt;2&lt;/sup&gt; + &lt;em&gt;y&lt;/em&gt;&lt;sup&gt;2&lt;/sup&gt; = 1. We can define a group operation on the points of the unit circle via angle addition. It’s easiest to describe with polar coordinates I suppose: cis(α) composed with cis(β) gives cis(α+β). Except for some reason we want (0,1) to be the identity so make that cis(α+β-π).&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;In Cartesian coordinates, if the two input points are (&lt;em&gt;x&lt;/em&gt;&lt;sub&gt;1&lt;/sub&gt;, &lt;em&gt;y&lt;/em&gt;&lt;sub&gt;1&lt;/sub&gt;) and (&lt;em&gt;x&lt;/em&gt;&lt;sub&gt;2&lt;/sub&gt;, &lt;em&gt;y&lt;/em&gt;&lt;sub&gt;2&lt;/sub&gt;) then we get (&lt;em&gt;x&lt;/em&gt;&lt;sub&gt;1&lt;/sub&gt; &lt;em&gt;y&lt;/em&gt;&lt;sub&gt;2&lt;/sub&gt; + &lt;em&gt;x&lt;/em&gt;&lt;sub&gt;2&lt;/sub&gt; &lt;em&gt;y&lt;/em&gt;&lt;sub&gt;1&lt;/sub&gt;, &lt;em&gt;y&lt;/em&gt;&lt;sub&gt;1&lt;/sub&gt; &lt;em&gt;y&lt;/em&gt;&lt;sub&gt;2&lt;/sub&gt; - &lt;em&gt;x&lt;/em&gt;&lt;sub&gt;1&lt;/sub&gt; &lt;em&gt;x&lt;/em&gt;&lt;sub&gt;2&lt;/sub&gt;). Let us denote this point by (&lt;em&gt;a&lt;/em&gt;, &lt;em&gt;b&lt;/em&gt;).&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;An Edwards curve is a deformed circle that is equivalent to an elliptic curve in some sense. There is some deal about requiring a point of order 4, which is probably related to the quadrilateral symmetry of the Edwards curve. It is parameterized by &lt;em&gt;d&lt;/em&gt; which I’m guessing is akin to the &lt;em&gt;j&lt;/em&gt;-invariant, and has the equation &lt;em&gt;x&lt;/em&gt;&lt;sup&gt;2&lt;/sup&gt; + &lt;em&gt;y&lt;/em&gt;&lt;sup&gt;2&lt;/sup&gt; = 1 + &lt;em&gt;d&lt;/em&gt; &lt;em&gt;x&lt;/em&gt;&lt;sup&gt;2&lt;/sup&gt; &lt;em&gt;y&lt;/em&gt;&lt;sup&gt;2&lt;/sup&gt;.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Using the above notation, group addition produces the point (&lt;em&gt;a&lt;/em&gt; / (1 + &lt;em&gt;D&lt;/em&gt;), &lt;em&gt;b&lt;/em&gt; / (1 - &lt;em&gt;D&lt;/em&gt;)) where &lt;em&gt;D&lt;/em&gt; = &lt;em&gt;d&lt;/em&gt; &lt;em&gt;x&lt;/em&gt;&lt;sub&gt;1&lt;/sub&gt; &lt;em&gt;x&lt;/em&gt;&lt;sub&gt;2&lt;/sub&gt; &lt;em&gt;y&lt;/em&gt;&lt;sub&gt;1&lt;/sub&gt; &lt;em&gt;y&lt;/em&gt;&lt;sub&gt;2&lt;/sub&gt;. Unlike elliptic curves, the same formula applies for identical inputs, and the also for the identity element: in paricular, there is no special formula for point doubling. From this equation one can write code to multiply points at breakneck speed.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Montgomery curves are given by &lt;em&gt;B&lt;/em&gt; &lt;em&gt;y&lt;/em&gt;&lt;sup&gt;2&lt;/sup&gt; = &lt;em&gt;x&lt;/em&gt;&lt;sup&gt;3&lt;/sup&gt; + &lt;em&gt;A&lt;/em&gt; &lt;em&gt;x&lt;/em&gt;&lt;sup&gt;2&lt;/sup&gt; + &lt;em&gt;x&lt;/em&gt;, and possess fast point addition with one big string attached: you can only add &lt;em&gt;P&lt;/em&gt; and &lt;em&gt;Q&lt;/em&gt; if you know &lt;em&gt;P - Q&lt;/em&gt;; this point forms the base of an addition "ladder". This is fine for exponentiations and hence ECDSA, but may be unsuitable for other cryptosystems.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;The details, and more, can be found at the &lt;a href="http://www.hyperelliptic.org/EFD"&gt;Explicit-Formulas Database&lt;/a&gt;. Also, see &lt;a href="http://cr.yp.to/ecdh.html"&gt;this highly optimized implementation of the curve 25519&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;&lt;strong&gt;DNSCurve&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;To Dan, speedy ECC is merely a means to an end: a brilliant and diabolical scheme to usurp &lt;a href="http://en.wikipedia.org/wiki/Dnssec"&gt;DNSSEC&lt;/a&gt; with &lt;a href="http://dnscurve.org/"&gt;DNSCurve&lt;/a&gt;. If DNSSEC does indeed have the drawbacks he described, then I wish him well. Kill it before it spreads! The Denial-of-Service attack amplification issue is enough to justify revoking its netizenship.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;&lt;strong&gt;Pairings&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;For &lt;a href="http://crypto.stanford.edu/pbc/"&gt;the PBC library&lt;/a&gt;, the good news is that Weierstrass curves are still the best known way to get at pairings (because of the order 4 point brouhaha), though research on alternate curves for pairing computation continues at a furious pace.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;The bad news is that the sample pairing parameters I’ve been distributing are mostly too weak. Additionally, Barreto-Naehrig curves, the least-optimized case in the library, are arguably now the most important pairing type.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;By the way, our discussion literally involved scribbling on a serviette:&lt;/p&gt;&lt;/div&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_8FH64FXxxnM/SpMRAGkae7I/AAAAAAAAYos/qQCOqpcR824/s1600-h/napkin1.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 300px; height: 400px;" src="http://4.bp.blogspot.com/_8FH64FXxxnM/SpMRAGkae7I/AAAAAAAAYos/qQCOqpcR824/s400/napkin1.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5373657473844149170" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_8FH64FXxxnM/SpMREN97r-I/AAAAAAAAYo0/Rn4Obf7fA40/s1600-h/napkin2.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 300px; height: 400px;" src="http://1.bp.blogspot.com/_8FH64FXxxnM/SpMREN97r-I/AAAAAAAAYo0/Rn4Obf7fA40/s400/napkin2.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5373657544549707746" /&gt;&lt;/a&gt;&lt;br /&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Alas, I am not a coauthor of this paper: the handwriting belongs exclusively to Bernstein and Lange.&lt;/p&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt;      &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-7501174394949732972?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/7501174394949732972/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=7501174394949732972' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7501174394949732972'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7501174394949732972'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2009/08/napkin-cryptography.html' title='Napkin Cryptography'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_8FH64FXxxnM/SpMRAGkae7I/AAAAAAAAYos/qQCOqpcR824/s72-c/napkin1.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-7604548114907875824</id><published>2009-08-13T01:12:00.002-07:00</published><updated>2011-04-06T01:49:02.334-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>Tune out</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_8FH64FXxxnM/SoPPZE1m90I/AAAAAAAAYno/6ibXA6Upc4U/s1600-h/benlynn.png"&gt;&lt;/a&gt;&lt;div style="text-align: left;"&gt;I had grand plans for &lt;a href="http://www-cs-students.stanford.edu/~blynn/spelltapper/"&gt;my Android game&lt;/a&gt;. Apart from actually completing it, I was going to fix bugs, and add sound and music.&lt;/div&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;div id="preamble"&gt;&lt;div class="sectionbody"&gt; &lt;div class="paragraph"&gt;&lt;p&gt;But my enthusiasm evaporated as fast as it had arrived, and now that I have forgotten the password to my key, it looks even less likely I’ll release new versions.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;I did get around to composing a tune for use somewhere in the game. Seeing as it almost certainly never find its way to the game, I may as well dump it here:&lt;/p&gt;&lt;p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_8FH64FXxxnM/SoPPZE1m90I/AAAAAAAAYno/6ibXA6Upc4U/s1600-h/benlynn.png"&gt;&lt;img src="http://4.bp.blogspot.com/_8FH64FXxxnM/SoPPZE1m90I/AAAAAAAAYno/6ibXA6Upc4U/s400/benlynn.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5369363210458036034" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 400px; height: 174px; " /&gt;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="paragraph"&gt;&lt;p style="text-align: left;"&gt;&lt;span class="Apple-style-span"  style="color:#0000EE;"&gt;I&lt;/span&gt; lacked the patience to learn how &lt;a href="http://lilypond.org/"&gt;LilyPond&lt;/a&gt; deals with "da capo al fine". Luckily it’s clear where it belongs; this is one of those A-A-B-A melodies.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Another problem was excessive whitespace in the generated PNG image. &lt;a href="http://benlynn.blogspot.com/2006/10/pi-music.html"&gt;My older post&lt;/a&gt; must have used &lt;tt&gt;lilypond-book&lt;/tt&gt; in its original incarnation, but that was back when I wrote raw icky HTML.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;There must be some &lt;tt&gt;lilypond&lt;/tt&gt; option to automatically crop the output, but I found manual labyrinthine. So instead, to produce the above image I ran something like:&lt;/p&gt;&lt;/div&gt; &lt;div class="literalblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;$ lilypond --png foo.ly&lt;br /&gt;$ convert -trim -border 8x8 -bordercolor white foo.png foo.png&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Other formats: &lt;a href="http://cs.stanford.edu/~blynn/20090813/benlynn.ly"&gt;benlynn.ly&lt;/a&gt; &lt;a href="http://cs.stanford.edu/~blynn/20090813/benlynn.ps"&gt;benlynn.ps&lt;/a&gt; &lt;a href="http://cs.stanford.edu/~blynn/20090813/benlynn.pdf"&gt;benlynn.pdf&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt;      &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-7604548114907875824?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/7604548114907875824/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=7604548114907875824' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7604548114907875824'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7604548114907875824'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2009/08/tune-out.html' title='Tune out'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_8FH64FXxxnM/SoPPZE1m90I/AAAAAAAAYno/6ibXA6Upc4U/s72-c/benlynn.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-9206672842530350365</id><published>2009-07-30T01:23:00.002-07:00</published><updated>2011-04-06T01:49:02.339-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='puzzles'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>ZDD fun</title><content type='html'>&lt;div xmlns='http://www.w3.org/1999/xhtml'&gt; &lt;div id='preamble'&gt; &lt;div class='sectionbody'&gt; &lt;div class='paragraph'&gt;&lt;p&gt;I finally released the source for &lt;a href='http://benlynn.blogspot.com/2009/05/logic-puzzles-vs-zdds.html'&gt;my logic puzzle solvers&lt;/a&gt; using &lt;a href='http://crypto.stanford.edu/pbc/notes/zdd/'&gt;ZDDs&lt;/a&gt;. I was hoping to finish at least the &lt;a href='http://en.wikipedia.org/wiki/Nurikabe'&gt;Nurikabe&lt;/a&gt; solver first, but I lost interest.&lt;/p&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;Mirrors:&lt;/p&gt;&lt;/div&gt; &lt;div class='ulist'&gt;&lt;ul&gt; &lt;li&gt; &lt;p&gt; &lt;a href='http://github.com/blynn/zddfun/tree/master'&gt;http://github.com/blynn/zddfun/tree/master&lt;/a&gt; &lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt; &lt;a href='http://repo.or.cz/w/zddfun.git'&gt;http://repo.or.cz/w/zddfun.git&lt;/a&gt; &lt;/p&gt; &lt;/li&gt; &lt;/ul&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;I used the code to help construct the following &lt;a href='http://en.wikipedia.org/wiki/Slither_Link'&gt;Slither Link puzzle&lt;/a&gt;:&lt;/p&gt;&lt;/div&gt; &lt;div class='listingblock'&gt; &lt;div class='content'&gt; &lt;pre&gt;&lt;tt&gt;2...23&lt;br/&gt;..3...&lt;br/&gt;..3..2&lt;br/&gt;.2.22.&lt;br/&gt;....22&lt;br/&gt;1..1.1&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class='paragraph'&gt;&lt;p&gt;ZDDs are fascinating data structures, and using them to solve a logic puzzle is more fun that the puzzle itself. I hope to revisit them some day.&lt;/p&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt;      &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-9206672842530350365?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/9206672842530350365/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=9206672842530350365' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/9206672842530350365'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/9206672842530350365'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2009/07/zdd-fun_30.html' title='ZDD fun'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-663297567998379120</id><published>2009-06-08T01:00:00.002-07:00</published><updated>2011-04-06T01:49:02.344-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Much Ado About Nothing</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt; &lt;div id="preamble"&gt; &lt;div class="sectionbody"&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Whitespace in XML files can behave unintuitively, especially when it comes to newlines. My script that converts AsciiDoc to Blogger posts entry is peppered with awful kludges meant to bring them in line, but still fails to indent source code.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;After much experimentation, and as little reading of the official XML specifications as possible, I have determined the best practices for my situation:&lt;/p&gt;&lt;/div&gt; &lt;div class="ulist"&gt;&lt;ul&gt; &lt;li&gt; &lt;p&gt; Adding &lt;tt&gt;xml:space="preserve"&lt;/tt&gt; to the opening &lt;tt&gt;content&lt;/tt&gt; tag preserves the   source code indentation. &lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt; It appears impossible to preserve newlines anywhere. They vanish   mysteriously, not even leaving a space behind. By default XML leaves a space,   so I don’t understand why; perhaps it’s an Atom thing. Thus I add   "&amp;lt;br /&amp;gt;" at end of each line where formatting matters, and prefix each   newline with a space. &lt;/p&gt; &lt;/li&gt; &lt;/ul&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;My updated script appears below, this time with indentation.&lt;/p&gt;&lt;/div&gt; &lt;div class="listingblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;if&lt;/span&gt;&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;[[&lt;/span&gt; -z &lt;span style="color: rgb(255, 0, 0);"&gt;"$1"&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;]]&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;;&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;then&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  echo Usage&lt;span style="color: rgb(153, 0, 0);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$0&lt;/span&gt; ASCIIDOC_SOURCE &lt;span style="color: rgb(153, 0, 0);"&gt;[&lt;/span&gt;LABELS&lt;span style="color: rgb(153, 0, 0);"&gt;...]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;  exit&lt;/span&gt;&lt;/span&gt; &lt;span style="color: rgb(153, 51, 153);"&gt;1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;if&lt;/span&gt;&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;[[&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;!&lt;/span&gt; -f &lt;span style="color: rgb(255, 0, 0);"&gt;"$1"&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;]]&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;;&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;then&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  echo &lt;span style="color: rgb(0, 153, 0);"&gt;$1&lt;/span&gt; not found&lt;span style="color: rgb(153, 0, 0);"&gt;.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;  exit&lt;/span&gt;&lt;/span&gt; &lt;span style="color: rgb(153, 51, 153);"&gt;1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;outfile&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$1&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;.&lt;/span&gt;xml&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;# Extract = Title =, which must be on a line by itself.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;title&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$(&lt;/span&gt;grep &lt;span style="color: rgb(255, 0, 0);"&gt;'^ *='&lt;/span&gt; -m &lt;span style="color: rgb(153, 51, 153);"&gt;1&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$1&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;|&lt;/span&gt; sed &lt;span style="color: rgb(255, 0, 0);"&gt;'s/^ *=* *//'&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;|&lt;/span&gt; sed &lt;span style="color: rgb(255, 0, 0);"&gt;'s/ *=* *$//'&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;# Hmm, this draft thing used to work, but not anymore.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;echo &lt;span style="color: rgb(255, 0, 0);"&gt;'&amp;lt;entry xmlns="http://www.w3.org/2005/Atom"&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;  &amp;lt;app:control xmlns:app="http://www.w3.org/2007/app"&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;    &amp;lt;app:draft&amp;gt;yes&amp;lt;/app:draft&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;  &amp;lt;/app:control&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;  &amp;lt;title type="text"&amp;gt;'&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$title&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;'&amp;lt;/title&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;  &amp;lt;content type="xhtml" xml:space="preserve"&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;    &amp;lt;div xmlns="http://www.w3.org/1999/xhtml"&amp;gt;'&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$outfile&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;# We need \n newlines for sed.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;asciidoc -a &lt;span style="color: rgb(0, 153, 0);"&gt;newline&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=\\&lt;/span&gt;n -s -o - &lt;span style="color: rgb(0, 153, 0);"&gt;$1&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$outfile&lt;/span&gt;&lt;br /&gt;echo &lt;span style="color: rgb(255, 0, 0);"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;    &amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;  &amp;lt;/content&amp;gt;'&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$outfile&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;while&lt;/span&gt;&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;[[&lt;/span&gt; -n &lt;span style="color: rgb(0, 153, 0);"&gt;$2&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;]];&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;do&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  echo &lt;span style="color: rgb(255, 0, 0);"&gt;'  &amp;lt;category scheme="http://www.blogger.com/atom/ns#" term="'"$2"'" /&amp;gt;'&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$outfile&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;  shift&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;done&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;echo &lt;span style="color: rgb(255, 0, 0);"&gt;'&amp;lt;/entry&amp;gt;'&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$outfile&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;# I can't figure out how to preserve line breaks in Atom XML,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;# hence the following.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;# Prefix each newline with a space.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;sed -i &lt;span style="color: rgb(255, 0, 0);"&gt;'s/$/ /'&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$outfile&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;# Add &amp;lt;br /&amp;gt; to the end of all lines between &amp;lt;pre&amp;gt; and &amp;lt;/pre&amp;gt;.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;sed -i &lt;span style="color: rgb(255, 0, 0);"&gt;'/&amp;lt;pre&amp;gt;/,/&amp;lt;&lt;/span&gt;&lt;span style="color: rgb(204, 51, 204);"&gt;\/&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;pre&amp;gt;/s/ *$/&amp;lt;br &lt;/span&gt;&lt;span style="color: rgb(204, 51, 204);"&gt;\/&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;&amp;gt;/'&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$outfile&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;# Undo what we just did for the &amp;lt;/pre&amp;gt; line.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;sed -i &lt;span style="color: rgb(255, 0, 0);"&gt;'s/&lt;/span&gt;&lt;span style="color: rgb(204, 51, 204);"&gt;\(&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: rgb(204, 51, 204);"&gt;\/&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;pre&amp;gt;.*&lt;/span&gt;&lt;span style="color: rgb(204, 51, 204);"&gt;\)&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;&amp;lt;br &lt;/span&gt;&lt;span style="color: rgb(204, 51, 204);"&gt;\/&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;&amp;gt;/&lt;/span&gt;&lt;span style="color: rgb(204, 51, 204);"&gt;\1&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;/'&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$outfile&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;if&lt;/span&gt;&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;[[&lt;/span&gt; -z &lt;span style="color: rgb(0, 153, 0);"&gt;$AUTH_TOKEN&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;]];&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;then&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  stty -echo&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;  read&lt;/span&gt;&lt;/span&gt; -p &lt;span style="color: rgb(255, 0, 0);"&gt;"Blogger password: "&lt;/span&gt; pw&lt;br /&gt;  stty echo&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  token&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$(&lt;/span&gt;curl --silent https&lt;span style="color: rgb(153, 0, 0);"&gt;:&lt;/span&gt;//www&lt;span style="color: rgb(153, 0, 0);"&gt;.&lt;/span&gt;google&lt;span style="color: rgb(153, 0, 0);"&gt;.&lt;/span&gt;com/accounts/ClientLogin &lt;span style="color: rgb(153, 0, 0);"&gt;\&lt;/span&gt;&lt;br /&gt;-d &lt;span style="color: rgb(0, 153, 0);"&gt;Email&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;benlynn@gmail&lt;span style="color: rgb(153, 0, 0);"&gt;.&lt;/span&gt;com -d &lt;span style="color: rgb(0, 153, 0);"&gt;Passwd&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;"$pw"&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;\&lt;/span&gt;&lt;br /&gt;-d &lt;span style="color: rgb(0, 153, 0);"&gt;accountType&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;GOOGLE &lt;span style="color: rgb(153, 0, 0);"&gt;\&lt;/span&gt;&lt;br /&gt;-d &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;source&lt;/span&gt;&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;asciidoc2blogger &lt;span style="color: rgb(153, 0, 0);"&gt;\&lt;/span&gt;&lt;br /&gt;-d &lt;span style="color: rgb(0, 153, 0);"&gt;service&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;blogger &lt;span style="color: rgb(153, 0, 0);"&gt;|&lt;/span&gt; grep Auth &lt;span style="color: rgb(153, 0, 0);"&gt;|&lt;/span&gt; cut -d &lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt; -f &lt;span style="color: rgb(153, 51, 153);"&gt;2&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;  AUTH_TOKEN&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$token&lt;/span&gt;&lt;br /&gt;  echo &lt;span style="color: rgb(0, 153, 0);"&gt;AUTH_TOKEN&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$token&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;# The URL was cut and pasted from &amp;lt;link rel="service.post"&amp;gt; from&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;# my blog's HTML source.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;curl --silent --request POST --data &lt;span style="color: rgb(255, 0, 0);"&gt;"@$outfile"&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;\&lt;/span&gt;&lt;br /&gt;--header &lt;span style="color: rgb(255, 0, 0);"&gt;"Content-Type: application/atom+xml"&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;\&lt;/span&gt;&lt;br /&gt;--header &lt;span style="color: rgb(255, 0, 0);"&gt;"Authorization: GoogleLogin auth=$AUTH_TOKEN"&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;\&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;"http://www.blogger.com/feeds/4222267598459829544/posts/default"&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;\&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;|&lt;/span&gt; tidy -xml -indent -quiet&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt; &lt;/div&gt;      &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-663297567998379120?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/663297567998379120/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=663297567998379120' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/663297567998379120'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/663297567998379120'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2009/06/much-ado-about-nothing.html' title='Much Ado About Nothing'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-3263573142516539314</id><published>2009-06-07T11:58:00.002-07:00</published><updated>2011-04-06T01:49:02.351-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>AsciiDoc To Blogger</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;div id="preamble"&gt; &lt;div class="sectionbody"&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Once I grew accustomed to writing &lt;a href="http://www.methods.co.nz/asciidoc/"&gt;AsciiDoc&lt;/a&gt;, editing even tiny amounts of HTML became bothersome. The sites I maintain use custom scripts to build HTML files from AsciiDoc source, but I have less control over this blog. Up until now I’ve been using the Blogger in-browser editor to fine-tune the markup in these posts.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;AsciiDoc’s author, Stuart Rackham, also wrote &lt;a href="http://srackham.wordpress.com/category/asciidoc/"&gt;a tool to go from AsciiDoc to a WordPress blog&lt;/a&gt;. Blogger should be similar, and perhaps even easier to work with, since WordPress appears to have a few quirks.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;My first thought was to use the &lt;a href="http://www.google.com/search?hl=en&amp;amp;q=mail-to-blogger"&gt;Mail-to-Blogger&lt;/a&gt; feature: I could run AsciiDoc on on the source, then send it to a particular email address to publish it. This attempt floundered becaues GMail has no raw HTML mode. Of course, I could script an SMTP server instead, but this seems excessive.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Next I considered the import and export feature. But even if I could figure out how to generate suitable XMLs, I’d have to click around and solve a CAPTCHA each time I imported a post.&lt;/p&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;Finally the simplest solution hit me: use the &lt;a href="http://code.google.com/apis/blogger/docs/2.0/developers_guide_protocol.html"&gt;Blogger Data API&lt;/a&gt;. With an HTTPS request or two, I can post raw HTML, set labels, and even choose whether to publish immediately or save as a draft. All it takes is a shell script &lt;a href="http://code.google.com/apis/gdata/articles/using_cURL.html"&gt;using curl with Google data services&lt;/a&gt;:&lt;br /&gt;&lt;/p&gt;&lt;/div&gt; &lt;div class="listingblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;if&lt;/span&gt;&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;[[&lt;/span&gt; -z &lt;span style="color: rgb(255, 0, 0);"&gt;"$1"&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;]]&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;;&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;then&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;echo Usage&lt;span style="color: rgb(153, 0, 0);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$0&lt;/span&gt; ASCIIDOC_SOURCE &lt;span style="color: rgb(153, 0, 0);"&gt;[&lt;/span&gt;LABELS&lt;span style="color: rgb(153, 0, 0);"&gt;...]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;exit&lt;/span&gt;&lt;/span&gt; &lt;span style="color: rgb(153, 51, 153);"&gt;1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;if&lt;/span&gt;&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;[[&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;!&lt;/span&gt; -f &lt;span style="color: rgb(255, 0, 0);"&gt;"$1"&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;]]&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;;&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;then&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;echo &lt;span style="color: rgb(0, 153, 0);"&gt;$1&lt;/span&gt; not found&lt;span style="color: rgb(153, 0, 0);"&gt;.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;exit&lt;/span&gt;&lt;/span&gt; &lt;span style="color: rgb(153, 51, 153);"&gt;1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;outfile&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$1&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;.&lt;/span&gt;xml&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;# Extract = Title =, which must be on a line by itself.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;title&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$(&lt;/span&gt;grep &lt;span style="color: rgb(255, 0, 0);"&gt;'^ *='&lt;/span&gt; -m &lt;span style="color: rgb(153, 51, 153);"&gt;1&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$1&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;|&lt;/span&gt; sed &lt;span style="color: rgb(255, 0, 0);"&gt;'s/^ *=* *//'&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;|&lt;/span&gt; sed &lt;span style="color: rgb(255, 0, 0);"&gt;'s/ *=* *$//'&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;# Hmm, this draft thing used to work, but not anymore.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;echo &lt;span style="color: rgb(255, 0, 0);"&gt;'&amp;lt;entry xmlns="http://www.w3.org/2005/Atom"&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;  &amp;lt;app:control xmlns:app="http://www.w3.org/2007/app"&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;    &amp;lt;app:draft&amp;gt;yes&amp;lt;/app:draft&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;  &amp;lt;/app:control&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;  &amp;lt;title type="text"&amp;gt;'&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$title&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;'&amp;lt;/title&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;  &amp;lt;content type="xhtml"&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;    &amp;lt;div xmlns="http://www.w3.org/1999/xhtml"&amp;gt;'&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$outfile&lt;/span&gt;&lt;br /&gt;asciidoc -f macros -s -o - &lt;span style="color: rgb(0, 153, 0);"&gt;$1&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$outfile&lt;/span&gt;&lt;br /&gt;echo &lt;span style="color: rgb(255, 0, 0);"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;    &amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;  &amp;lt;/content&amp;gt;'&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$outfile&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;while&lt;/span&gt;&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;[[&lt;/span&gt; -n &lt;span style="color: rgb(0, 153, 0);"&gt;$2&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;]];&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;do&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;echo &lt;span style="color: rgb(255, 0, 0);"&gt;'  &amp;lt;category scheme="http://www.blogger.com/atom/ns#" term="'"$2"'" /&amp;gt;'&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$outfile&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;shift&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;done&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;echo &lt;span style="color: rgb(255, 0, 0);"&gt;'&amp;lt;/entry&amp;gt;'&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$outfile&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;# I can't figure out how to preserve line breaks in Atom XML, hence the&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;# following workaround.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;sed -i &lt;span style="color: rgb(255, 0, 0);"&gt;'/&amp;lt;pre&amp;gt;/,/&amp;lt;&lt;/span&gt;&lt;span style="color: rgb(204, 51, 204);"&gt;\/&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;pre&amp;gt;/a&amp;lt;br &lt;/span&gt;&lt;span style="color: rgb(204, 51, 204);"&gt;\/&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;&amp;gt;'&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;$outfile&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;if&lt;/span&gt;&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;[[&lt;/span&gt; -z &lt;span style="color: rgb(0, 153, 0);"&gt;$AUTH_TOKEN&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;]];&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;then&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;stty -echo&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;read&lt;/span&gt;&lt;/span&gt; -p &lt;span style="color: rgb(255, 0, 0);"&gt;"Blogger password: "&lt;/span&gt; pw&lt;br /&gt;stty echo&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;token&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$(&lt;/span&gt;curl --silent https&lt;span style="color: rgb(153, 0, 0);"&gt;:&lt;/span&gt;//www&lt;span style="color: rgb(153, 0, 0);"&gt;.&lt;/span&gt;google&lt;span style="color: rgb(153, 0, 0);"&gt;.&lt;/span&gt;com/accounts/ClientLogin &lt;span style="color: rgb(153, 0, 0);"&gt;\&lt;/span&gt;&lt;br /&gt;-d &lt;span style="color: rgb(0, 153, 0);"&gt;Email&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;benlynn@gmail&lt;span style="color: rgb(153, 0, 0);"&gt;.&lt;/span&gt;com -d &lt;span style="color: rgb(0, 153, 0);"&gt;Passwd&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;"$pw"&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;\&lt;/span&gt;&lt;br /&gt;-d &lt;span style="color: rgb(0, 153, 0);"&gt;accountType&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;GOOGLE &lt;span style="color: rgb(153, 0, 0);"&gt;\&lt;/span&gt;&lt;br /&gt;-d &lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;source&lt;/span&gt;&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;asciidoc2blogger &lt;span style="color: rgb(153, 0, 0);"&gt;\&lt;/span&gt;&lt;br /&gt;-d &lt;span style="color: rgb(0, 153, 0);"&gt;service&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;blogger &lt;span style="color: rgb(153, 0, 0);"&gt;|&lt;/span&gt; grep Auth &lt;span style="color: rgb(153, 0, 0);"&gt;|&lt;/span&gt; cut -d &lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt; -f &lt;span style="color: rgb(153, 51, 153);"&gt;2&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;AUTH_TOKEN&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$token&lt;/span&gt;&lt;br /&gt;echo &lt;span style="color: rgb(0, 153, 0);"&gt;AUTH_TOKEN&lt;/span&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;$token&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;# The URL was cut and pasted from &amp;lt;link rel="service.post"&amp;gt; from my&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="color: rgb(154, 25, 0);"&gt;# blog's HTML source.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;curl --silent --request POST --data &lt;span style="color: rgb(255, 0, 0);"&gt;"@$outfile"&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;\&lt;/span&gt;&lt;br /&gt;--header &lt;span style="color: rgb(255, 0, 0);"&gt;"Content-Type: application/atom+xml"&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;\&lt;/span&gt;&lt;br /&gt;--header &lt;span style="color: rgb(255, 0, 0);"&gt;"Authorization: GoogleLogin auth=$AUTH_TOKEN"&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;\&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;"http://www.blogger.com/feeds/4222267598459829544/posts/default"&lt;/span&gt; &lt;span style="color: rgb(153, 0, 0);"&gt;\&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(153, 0, 0);"&gt;|&lt;/span&gt; tidy -xml -indent -quiet&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="paragraph"&gt;&lt;p&gt;Actually, that’s not quite all: to work around another weird XML whitespace issue I use the following AsciiDoc &lt;tt&gt;macros&lt;/tt&gt; file.&lt;/p&gt;&lt;/div&gt; &lt;div class="listingblock"&gt; &lt;div class="content"&gt; &lt;pre&gt;&lt;tt&gt;[miscellaneous]&lt;br /&gt;newline=" \n"&lt;/tt&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;div class="paragraph"&gt;&lt;p&gt;We insert a space before each newline to prevent words separated by a line break from being joined together.&lt;/p&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt;     &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-3263573142516539314?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/3263573142516539314/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=3263573142516539314' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3263573142516539314'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3263573142516539314'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2009/06/asciidoc-to-blogger.html' title='AsciiDoc To Blogger'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-1897979991254145070</id><published>2009-05-31T14:34:00.001-07:00</published><updated>2011-04-06T01:49:02.355-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='puzzles'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Logic puzzles vs ZDDs</title><content type='html'>I've been addicted to logical puzzles for a long time. Recently, I've become addicted to writing programs to solve addicting logic puzzles.&lt;br /&gt;&lt;br /&gt;Years ago I whipped up a &lt;a href="http://en.wikipedia.org/wiki/Dancing_links"&gt;dancing links&lt;/a&gt; sudoku solver as popularized by Knuth, which converts sudoku instances into exact cover problems. So of course when I learned &lt;a href="http://crypto.stanford.edu/pbc/notes/zdd/"&gt;ZDDs&lt;/a&gt; were good at exact cover problems, again from Knuth, I immediately thought of sudokus.&lt;br /&gt;&lt;br /&gt;I dreamt of building a ZDD representing all possible (standard) sudokus. I could then fill in some boxes, and from here, I could count the number of solutions, pick one at random uniformly, or weight the solutions with any given scheme and find the maximum or minimum solution, as well as the mean and standard deviation.&lt;br /&gt;&lt;br /&gt;I eagerly wrote enough ZDD routines to encode the sudoku constraints. The preliminary results were encouraging: my ZDD enforcing exactly one per box and exactly one per row for each digit required only 20738 nodes. But the column constraints crushed my aspirations. No matter how I sliced it, the ZDD grew too rapidly as more constraints were added.&lt;br /&gt;&lt;br /&gt;As a consolation prize, I hoped to at least convert my program into a solver that would rival my earlier program. Unfortunately, although the end result could solve a sudoku, it did so slowly.&lt;br /&gt;&lt;br /&gt;Deflated, I sought reassurance by verifying Knuth's results on chessboard tilings. Thankfully my numbers agreed exactly, so I knew my implementation was sufficiently correct and efficient for basic experiments. On the other hand, it also probably means that sudokus are indeed beyond the reach of ZDDs.&lt;br /&gt;&lt;br /&gt;I flailed around looking for another excuse to use ZDDs. It didn't take long: &lt;a href="http://en.wikipedia.org/wiki/Nonogram"&gt;nonogram&lt;/a&gt; puzzles turn out to be no match for their power.&lt;br /&gt;&lt;br /&gt;By the way, I obviously mean human-friendly nonograms, as the general problem is NP-complete; ZDDs aren't &lt;i&gt;that&lt;/i&gt; revolutionary! The same applies to other NP-complete puzzles: rather than prove P = NP, my goal is to write an efficient solver for instances meant for humans, and maybe also instances that are just a bit tougher.&lt;br /&gt;&lt;br /&gt;I didn't have much time to stress test the nonogram solver, as I rapidly moved on to a &lt;a href="http://en.wikipedia.org/wiki/Light_Up"&gt;Light Up&lt;/a&gt; solver. Shortly after I threw together a &lt;a href="http://www.google.com/search?q=dominosa"&gt;dominosa&lt;/a&gt; solver, and yet more ZDD solvers for other logic puzzles are in the pipeline. I'll release the code soon.&lt;br /&gt;&lt;br /&gt;As dominosa puzzles are easily disguised as exact cover problems, I also wrote a dancing links solver. Unsurprisingly, it is much faster; I'm sure specialized recursive search programs outperform ZDDs for the other puzzles too, but ZDDs have some advantages. Firstly, they are easy to write once the infrastructure is in place. Constraints for many logic puzzles are easily expressed in terms of ZDDs. Secondly, even if ZDDs are only practical for smaller instances, they can often answer more questions.&lt;br /&gt;&lt;br /&gt;For example, how many dominosa problems are there if we go up to double-nine? How many are there if we then place a few particular dominoes in certain places? What does puzzle number 12345 look like? And so on.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-1897979991254145070?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/1897979991254145070/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=1897979991254145070' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1897979991254145070'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1897979991254145070'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2009/05/logic-puzzles-vs-zdds_31.html' title='Logic puzzles vs ZDDs'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-4619260355246045847</id><published>2009-05-24T19:09:00.001-07:00</published><updated>2011-04-06T01:49:02.359-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>A really fundamental data structure</title><content type='html'>There are many things I wish my textbooks had taught me. It's often not the fault of the authors: in any field, even the best ideas suffer a lag between discovery and dissemination. However, even accounting for this, it seems I'm always behind the times. Some glaring omissions I belatedly redressed:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://benlynn.blogspot.com/2006/02/calculus-using-infinitesimals.html"&gt;Calculus can be taught rigorously with infinitesimals&lt;/a&gt;, following the path traveled by its original developers, unlike the approach using limits and deltas and epsilons.&lt;/li&gt;&lt;li&gt;Cuckoo hashing gives &lt;a href="http://www.ru.is/faculty/ulfar/CuckooHash.pdf"&gt;hash tables with O(1) worst-case lookups and are also space-efficient&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;a href="http://burtleburtle.net/bob/hash/index.html#lookup"&gt;Bob Jenkin's hash functions&lt;/a&gt; for hash tables are better than those many commonly encountered in textbooks.&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Perfect_hash"&gt;Perfect hashes&lt;/a&gt; give optimal performance when the keys come from a small set that can be preprocessed beforehand.&lt;/li&gt;&lt;li&gt;&lt;a href="http://cr.yp.to/critbit.html"&gt;Crit-bit trees&lt;/a&gt; perform similarly to hash tables but have some advantages. My favourites: good worst-case running time and lexicographical operations. Apparently they can be used to &lt;a href="http://en.wikipedia.org/wiki/Burstsort"&gt;sort strings faster than quicksort or radix sort&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Aa_trees"&gt;AA trees&lt;/a&gt; remove the pain from implementing balanced binary trees.&lt;/li&gt;&lt;/ul&gt;But most of all, I bemoan my ignorance of binary decision diagrams (BDDs). In his Computer Musings lecture, Don Knuth states "it's one of the only really fundamental data structures that came out in the last 25 years". He felt it was so important that he devoted the following lecture to ZDDs, a close relative of BDDs.&lt;br /&gt;&lt;br /&gt;Take a map of the USA, and consider the following problems:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Suppose you want to visit every state capitol exactly once, traveling on highways. Which route is shortest? Which route is longest? What is the mean and standard deviation of all the routes?&lt;/li&gt;&lt;li&gt;Weight the states by any means, for example, by reading their two-letter code as a number in base 36. Then find a set of states such that no two members of the set are adjacent, and the total weight is maximized.&lt;/li&gt;&lt;li&gt;There are several ways to colour the map with four colours such that no two adjacent states have the same colour. How do we pick one of these colourings at random (uniformly)?&lt;/li&gt;&lt;/ol&gt;Or consider these:&lt;br /&gt;&lt;ol start="4"&gt;&lt;li&gt;How many ways can you tile a chessboard using 1x1, 2x1 and 3x1 rectangles? What if we also have pieces that look like 2x2 boxes with one tile missing?&lt;/li&gt;&lt;li&gt;List all 5-letter words that remain words after replacing a 'b' with 'o'.&lt;/li&gt;&lt;/ol&gt;Do you know how to solve these efficiently? If not, you might want to read up on BDDs, particularly &lt;a href="http://www-cs-staff.stanford.edu/%7Euno/news.html"&gt;Knuth's treatment, which has just hit the presses&lt;/a&gt;, and &lt;a href="http://scpd.stanford.edu/knuth/index.jsp"&gt;watch his lectures&lt;/a&gt;. Using these sources, I took &lt;a href="http://crypto.stanford.edu/pbc/notes/zdd/"&gt;some notes on ZDDs&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Learning about BDDs reminded me of learning about &lt;a href="http://crypto.stanford.edu/pbc/notes/polya/"&gt;Pólya theory&lt;/a&gt;, another simple yet powerful technique which transforms the seemingly impossible into child's play.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-4619260355246045847?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/4619260355246045847/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=4619260355246045847' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4619260355246045847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4619260355246045847'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2009/05/really-fundamental-data-structure_24.html' title='A really fundamental data structure'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-1827255001113354039</id><published>2009-05-17T11:05:00.001-07:00</published><updated>2011-04-08T19:05:24.016-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Assorted sorts</title><content type='html'>Let &lt;tt&gt;void x(int *p, int *q)&lt;/tt&gt; be function that swaps its arguments, namely, after execution, &lt;tt&gt;*p&lt;/tt&gt; is equal to the original value of &lt;tt&gt;*q&lt;/tt&gt; and vice versa.&lt;br /&gt;
&lt;br /&gt;
What do the following functions do?&lt;br /&gt;
&lt;pre&gt;void f(int *m, int *n) {
int *p;
for(p = m; p &amp;lt; n; p -= *p &amp;gt; p[1] ? x(p, p + 1), p != m : -1);
}

void f(int *m, int *n) {
int *p, *q;
for(p = m; p &amp;lt;= n; p++) for(q = p; q &amp;lt;= n; *p &amp;gt; *q ? x(p, q) : 0, q++);
}

void f(int *m, int *n) {
  int i, j, k;
  for(i = 1; (k = i) &amp;lt;= n - m; i++)
    while(k &amp;amp;&amp;amp; m[j = k - 1 &amp;gt;&amp;gt; 1] &amp;lt; m[k]) x(m + j, m + k), k = j;
  for(k--; x(m, m + k--), k;) for(i = 0;
      j = 2 * i + 1, k &amp;gt;= (j += k &amp;gt; j &amp;amp;&amp;amp; m[j] &amp;lt;= m[j + 1]) &amp;amp;&amp;amp; m[j] &amp;gt; m[i];
      x(m + j, m + i), i = j);
}

void f(int *m, int *n) {
int *p, *q, i;
do
for (i = 0, p = q = m, ++q; q &amp;lt;= n; p = q++) *p &amp;gt; *q ? x(p, q), i = 1 : 0;
while(i);
}

void f(int *m, int *n) {
int *p, *q;
for(p = m + 1, q = n; p &amp;lt; q; *p &amp;gt; *m ? x(p, q--) : *p++);
m != n ? *m &amp;gt; *p ? x(m, p) : 0, f(m, --p), f(q, n) : 0;
}

void f(int *m, int *n) {
int *p, *q, i, j;
for(p = m + 1; p &amp;lt;= n; p++, *q = i)
for(i = *p, q = p; q &amp;gt; m &amp;amp;&amp;amp; i &amp;lt; (j = *(q - 1)); *q-- = j);
}

void f(int *m, int *n) {
static short s[] = { 1, 4, 10, 23, 57, 132, 301, 701, 1750 };
int *p, *q, i, j, a, b;
for(a = sizeof(s)/sizeof(short) - 1; a &amp;amp;&amp;amp; s[a] &amp;gt; n - m; a--);
while(a &amp;gt;= 0) for(p = m + (b = s[a--]); p &amp;lt;= n; p++, *q = i)
for(i = *p, q = p; q &amp;gt; m &amp;amp;&amp;amp; i &amp;lt; (j = *(q - b)); *q = j, q -= b);
}&lt;/pre&gt;
&lt;br /&gt;
The title is a big hint, but if you're still not sure, experiment with a function by compiling it with the following code, and supply some integers on standard input.&lt;br /&gt;
&lt;pre&gt;#include &amp;lt;stdio.h&amp;gt;

void f(int *m, int *n);

void x(int *p, int *q) {
  int i = *p;
  *p = *q, *q = i;
}

int main() {
  enum { limit = 8192 };
  int a[limit], *n, *k;
  for(n = a; 1 == scanf("%d", n) &amp;amp;&amp;amp; n - a &amp;lt; limit; n++);
  puts("in:");
  for (k = a; k &amp;lt; n; k++) printf(" %d", *k);
  puts("");
  f(a, n - 1);
  puts("out:");
  for (k = a; k &amp;lt; n; k++) printf(" %d", *k);
  puts("");
  return 0;
}&lt;/pre&gt;
&lt;br /&gt;
Can you identify each algorithm? The answers are, in some order, bubble sort, &lt;a href="http://www.cs.vu.nl/~dick/gnomesort.html"&gt;gnome sort&lt;/a&gt;, insertion sort, selection sort, shell sort, heapsort and quicksort.&lt;br /&gt;
&lt;br /&gt;
The real puzzle is why I bothered. All I can say is you don't have to be a chef to enjoy making spaghetti: see the &lt;a href="http://www.ioccc.org/"&gt;International Obfuscated C Code Contest&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-1827255001113354039?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/1827255001113354039/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=1827255001113354039' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1827255001113354039'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1827255001113354039'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2009/05/assorted-sorts_17.html' title='Assorted sorts'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-4702226307013157730</id><published>2009-05-14T19:08:00.001-07:00</published><updated>2011-04-06T01:49:02.369-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><title type='text'>Notable Notation</title><content type='html'>Pursuing a graduate degree in computer science at Stanford offered many perks. I benefited from at least one more than some of my colleagues, as I was assigned an office only a few doors away from &lt;a href="http://www-cs-faculty.stanford.edu/%7Eknuth/"&gt;Don Knuth&lt;/a&gt;'s.&lt;br /&gt;&lt;br /&gt;I'd occasionally catch a glimpse of the legendary computer scientist, for some reason usually donning a bicycle helmet. However, the proximity of his office alone seems to have had an effect: I switched to &lt;a href="http://www.latex-project.org/"&gt;LaTeX&lt;/a&gt; for typesetting. As an undergraduate I loyally used &lt;a href="http://www.qtrac.eu/lout.html"&gt;Lout&lt;/a&gt;, whose author, &lt;a href="http://www.it.usyd.edu.au/%7Ejeff/"&gt;Jeff Kingston&lt;/a&gt;, had an office near where I worked. In seriousness, it was probably more for practical reasons than out of respect: all my co-authors used LaTeX.&lt;br /&gt;&lt;br /&gt;I still have a soft spot for Lout with its clean and comprehensible internals. Compare with the inscrutable black-magic LaTeX style sheets built on top of TeX, which itself is tricky to grasp. However, the TeX equation syntax certainly deserves its status as the lingua franca of mathematicians communicating via email and other plain text mediums.&lt;br /&gt;&lt;br /&gt;My workplace is no longer near Knuth or Kingston. Perhaps not entirely coincidentally, I no longer use TeX or Lout. Wanting documentation sources to be as readable as much as possible, I devised a markup language inspired by venerable ASCII typesetting tricks of heavy email users and README authors, such as *asterisks*, _underscores_, and ==Headings==, and wrote a script to translate it to HTML. Luckily I didn't get far before discovering &lt;a href="http://www.methods.co.nz/asciidoc/"&gt;AsciiDoc&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Equation-heavy notes, books, HTML, PDF: AsciiDoc handles them all despite using source files that look like ordinary text documents. Easy to learn, easy to type, and easy on the eyes. Fine-tuning the final layout is difficult, a blessing in disguise as users have solid excuses for ignoring those typesetting nitpicks TeXperts are expected to correct.&lt;br /&gt;&lt;br /&gt;But I digress: back to name-dropping Donald E. Knuth. Although he was warm and friendly, I only spoke to him a few times as he was also busy. However, I had many chances to listen to him. Sometimes he'd give a talk in the meeting area outside my door, where I always had a guaranteed seat: I could simply wheel my office chair a few meters.&lt;br /&gt;&lt;br /&gt;Regrettably I recall almost nothing. There was a story about how noticing a pattern in table of numbers yielded a PhD thesis; he made it sound so easy. I also vaguely remember a fascinating introduction to coroutines along with an obscure but accessible application: &lt;a href="http://scholar.google.com/scholar?q=knuth+gray+code+coroutines"&gt; generating certain Gray code sequences&lt;/a&gt;. This would describe all I have left from Knuth's talks, but happily, some of his &lt;a href="http://www-cs-faculty.stanford.edu/%7Eknuth/musings.html"&gt; Computer Musings&lt;/a&gt; were recorded and are &lt;a href="http://scpd.stanford.edu/knuth/index.jsp"&gt; freely available online&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://stanford-online.stanford.edu/seminars/knuth/031017-knuth-100.asx"&gt;Notation&lt;/a&gt; episode is surprisingly interesting, especially the historical tidbits. The notation for &lt;a href="http://en.wikipedia.org/wiki/Floor_function"&gt; floor and ceiling functions&lt;/a&gt; only became standard relatively recently, a brilliant idea that usurped many inferior schemes and rapidly took over the world. Another young fad is to use "lg" for the base 2 logarithm, though now it seems &lt;a href="http://en.wikipedia.org/wiki/Logarithm"&gt;we should use "lb" instead&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The highlight was Iverson's bracket convention: one places an expression within square brackets, and the whole thing evaluates to 1 if the expression is true, and 0 otherwise. For example, [&lt;i&gt;k&lt;/i&gt; is even] means 1 when &lt;i&gt;k&lt;/i&gt; is even, and 0 otherwise. Other examples:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;sign(&lt;i&gt;x&lt;/i&gt;) = [&lt;i&gt;x&lt;/i&gt; &amp;gt; 0] - [&lt;i&gt;x&lt;/i&gt; &amp;lt; 0]&lt;/li&gt;&lt;li&gt;[A and B] + [A or B] = [A] + [B]&lt;/li&gt;&lt;/ul&gt;&lt;a href="http://scholar.google.com/scholar?q=%22two+notes+on+notation%22"&gt;"Two notes on notation"&lt;/a&gt; describes how to harness the power of the square bracket.&lt;br /&gt;&lt;br /&gt;This should all sound familiar to C programmers, because many C operators evaluate to 1 when the result is true and 0 otherwise. It's simultaneously disheartening and amusing that while mathematicians and computer scientists are discovering the utility of this convention, some programming language designers actively oppose it by introducing a boolean data type that cannot be interchanged with integers.&lt;br /&gt;&lt;br /&gt;The lecture was crammed with other ideas that deserve to be widespread. &lt;a href="http://crypto.stanford.edu/pbc/notes/misc/notation.xhtml"&gt;I discuss them&lt;/a&gt; somewhere I can use AsciiDoc and LaTeX equation notation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-4702226307013157730?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/4702226307013157730/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=4702226307013157730' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4702226307013157730'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4702226307013157730'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2009/05/notable-notation_14.html' title='Notable Notation'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-7042915255687525694</id><published>2009-02-11T04:46:00.001-08:00</published><updated>2011-04-06T01:49:02.372-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Compiler Compilers</title><content type='html'>Long ago I sat a set of exams designed to test the breadth of our computer science knowledge. I failed Compilers. Luckily, they had just changed the rules, and it was acceptable to fail Compilers provided you passed the Databases exam. Fortune smiled upon me, and I passed the Databases exam despite possessing even less knowledge in that field. Or perhaps fortune was actually laughing at me: I stayed away from compiler theory for the rest of my degree to my detriment. It's scandalous I still know so little.&lt;br /&gt;&lt;br /&gt;I was especially dispirited by my failure since I had thought I knew compilers well. When I was a child, I felt moved after learning about &lt;a href="http://en.wikipedia.org/wiki/Recursive_descent"&gt;recursive descent parsers&lt;/a&gt;. I had been mystified: how could a program could possibly read an expression like (1+2)*((3+4)/5)? All I could think of was to use a simple loop to iterate over the tokens in the expression and assigning them to local variables before processing them. This led nowhere, because the nesting of parentheses can be arbitrarily deep. Thus I was awestruck by the elegance of recursive descent; after the first reading the technique was indelibly etched in my mind.&lt;br /&gt;&lt;br /&gt;Much later, in university, basic compiler theory (grammar, languages, automata, and so on) came easily to me. I implemented a compiler for a toy language which produced the smallest and fastest code in my class. I'd also written the odd recursive descent parser for pet projects.&lt;br /&gt;&lt;br /&gt;So I thought I was a compiler expert. But I should have paid attention to my weaknesses. I found &lt;a href="http://en.wikipedia.org/wiki/Canonical_LR_parser"&gt;LR(1) parsers&lt;/a&gt; unintuitive. I leafed through their proofs of correctness and promptly forgot their details, especially since the textbook stated these parsers are rarely coded by hand and instead generated by programs such as yacc or &lt;a href="http://www.gnu.org/software/bison/"&gt;bison&lt;/a&gt;. Instead of embracing these powerful tools, I stuck with trusty hand-coded parsers because I understood them completely.&lt;br /&gt;&lt;br /&gt;Ultimately this caused me to fail my Compilers exam, which was peppered with yacc questions, and more seriously, to waste copious amounts of time coding and debugging parsers and lexical analyzers. I have at last invested energy in seriously learning &lt;a href="http://www.gnu.org/software/flex/"&gt;flex&lt;/a&gt; and bison, and I urge anyone in a similar situation to do the same. All these years, instead of writing recursive descent parsers unless there was a compelling reason not to, my default attitude should have been to use flex and bison unless there was a compelling reason not to.&lt;br /&gt;&lt;br /&gt;I wrote a compiler that converts a toy language (essentially a tiny subset of &lt;a href="http://en.wikipedia.org/wiki/Limbo_(programming_language)"&gt;Limbo&lt;/a&gt;) to Dalvik bytecode. I also wrote a Dalvik assembler along the way. In both cases, flex and bison were invaluable: gone are the finicky routines I've written and rewritten a million times, like handling an unexpected end of input, and fine-tuning mutually recursive functions and logic to get the desired operator precedence and associativity. &lt;br /&gt;&lt;br /&gt;I've put my efforts on hold. I was hoping for a relatively functional and less object-oriented input language, but it seems such a language would be tricky to shoehorn into Dalvik bytecode: function calls are abstracted, and with no control over stack frames I doubt I can implement &lt;a href="http://gcc.gnu.org/onlinedocs/gccint/Trampolines.html"&gt;trampolining&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Also, I greatly underestimated my ignorance of compiler theory. I know nothing about the &lt;a href="http://en.wikipedia.org/wiki/Static_single_assignment_form"&gt;static single assignment (SSA) form&lt;/a&gt;, which appears too new for the textbooks I had. Moreover, I need to refresh my memory on older optimization techniques.&lt;br /&gt;&lt;br /&gt;On the other hand, I don't want to discard my code, particularly as it will forever remind me to consider flex/bison before coding yet another parser by hand. Below is the git repository containing my primitive compiler and assembler that turns this:&lt;pre&gt;init() {&lt;br /&gt;  print("Hello, World");&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;on_input(arg : string) {&lt;br /&gt;  a: int;&lt;br /&gt;  a:= parse_int(arg);&lt;br /&gt;  print("\n");&lt;br /&gt;  print(int_to_string(a * (a + 1) / 2));&lt;br /&gt;}&lt;/pre&gt;into an Android app.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://cs.stanford.edu/~blynn/bender.git"&gt;http://cs.stanford.edu/~blynn/bender.git&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-7042915255687525694?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/7042915255687525694/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=7042915255687525694' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7042915255687525694'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7042915255687525694'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2009/02/compiler-compilers_11.html' title='Compiler Compilers'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-2965692667949445980</id><published>2009-02-06T04:46:00.001-08:00</published><updated>2011-04-06T01:49:02.378-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Minimal Dalvik Executables</title><content type='html'>When I was a kid, the Windows era had yet to arrive. Floor-model PCs in shops ran MS-DOS. They'd often take steps to stop little imps like me messing around with their systems. I recall one where they even deleted the standard DOS utilities. But they had left COMMAND.COM unmodified which is enough for plenty of mischief. One can create executables via:&lt;pre&gt; $ copy con: hello.com&lt;/pre&gt;and then enter machine code by holding Alt while typing a number on the keypad. (It doesn't work on &lt;a href="http://www.dosbox.com/"&gt;DOSBox&lt;/a&gt; by the way.) Some numbers, such as 0, must be avoided, but there are workarounds; the book from which I learned the trick, Ralph Burger's "Computer Viruses: a High-tech Disease", recommended first creating a program that read numbers in ASCII and output the corresponding binary, a sort of proto-assembler, which in turn could be used to create more complex executables.&lt;br /&gt;&lt;br /&gt;I created a HELLO.COM program by typing the following, where the numbers are typed on the numberpad with the Alt key held: &lt;pre&gt; 180 9 190 9 1 205 33 205 32 hello$&lt;/pre&gt; which is perhaps more recognizable to DOS programmers when converted to hex:&lt;br /&gt;&lt;pre&gt; B409 BA0901 CD21 CD20 hello$&lt;/pre&gt;It is the machine code for:&lt;pre&gt;MOV AH, 09&lt;br /&gt;MOV DX, 109h&lt;br /&gt;INT 21h&lt;br /&gt;INT 20h&lt;/pre&gt;This instructs DOS to print the '$'-terminated string at address 0x109 and then exit. When .COM files are loaded, DOS places their contents at address 0x100 (locations 0 to 0x100 are populated with miscellaneous information such as the command-line), hence the 0x109.&lt;br /&gt;&lt;br /&gt;Thus with 9 bytes plus the size of the message, plus a byte for the terminator, we get a valid DOS program. We can type raw machine code from the keyboard to produce a full program.&lt;br /&gt;&lt;br /&gt;Linux on x86 systems is less straightforward. The machine code is similar, but there must also be an ELF header (not strictly true: in some circumstances, &lt;a href="http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html"&gt;the entire machine code can be placed within a truncated ELF header!&lt;/a&gt;), making binaries more tedious to construct by hand. However, certain kernel configurations allow different header types, some of them brief enough to be easily memorized and hand-rolled.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Minimalist Android&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;How about Android? An APK file is a ZIP file containing, among other files, the Dalvik executable in a DEX file. We're interested in minimizing this DEX file, whose &lt;a href="http://sites.google.com/site/io/dalvik-vm-internals/Dalvik_Handouts.zip"&gt;full specification is well-documented&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;All Android code must lie in some class, and Android applications typically inherit from a library class such as Activity, so we must also record information such as the class name, access rights, and inheritance. This, in turn, requires a strings section so we can store the class names, as well as invoked library functions and function signatures. Other overheads include the header and the map section. We can cheat a little by defining the UI with XML, so it won't contribute to the size of the DEX file.&lt;br /&gt;&lt;br /&gt;After compiling to Java class files, most or all the R*.class files can be removed before making a DEX file,since the constants they contain are hardcoded in other class files by javac. For some projects, R$styleable must be retained, though this class appears to have since been removed from the public API.&lt;br /&gt;&lt;br /&gt;I chose to write a program that parsed the DEX file from the HelloAndroid example and output only the necessary sections rather than attempt to create a DEX file from scratch. I found I could safely elide the source filenames, debug information and annotations. The result consists of 632 bytes, though we could trivially shrink this further by using a shorter name for the class.&lt;pre&gt;0000000: 6465 780a 3033 3500 2f4f 153b 3623 8747  dex.035./O.;6#.G&lt;br /&gt;0000010: 6d02 4697 5b1e 959d a8b1 2f0f 9c3a a14f  m.F.[...../..:.O&lt;br /&gt;0000020: 7802 0000 7000 0000 7856 3412 0000 0000  x...p...xV4.....&lt;br /&gt;0000030: 0000 0000 f001 0000 0a00 0000 7000 0000  ............p...&lt;br /&gt;0000040: 0500 0000 9800 0000 0300 0000 ac00 0000  ................&lt;br /&gt;0000050: 0000 0000 0000 0000 0500 0000 d000 0000  ................&lt;br /&gt;0000060: 0100 0000 f800 0000 6001 0000 1801 0000  ........`.......&lt;br /&gt;0000070: 6201 0000 6a01 0000 6d01 0000 8501 0000  b...j...m.......&lt;br /&gt;0000080: 9a01 0000 bc01 0000 bf01 0000 c301 0000  ................&lt;br /&gt;0000090: c701 0000 d101 0000 0100 0000 0200 0000  ................&lt;br /&gt;00000a0: 0300 0000 0400 0000 0500 0000 0500 0000  ................&lt;br /&gt;00000b0: 0400 0000 0000 0000 0600 0000 0400 0000  ................&lt;br /&gt;00000c0: 5401 0000 0700 0000 0400 0000 5c01 0000  T...........\...&lt;br /&gt;00000d0: 0100 0000 0000 0000 0100 0200 0800 0000  ................&lt;br /&gt;00000e0: 0300 0000 0000 0000 0300 0200 0800 0000  ................&lt;br /&gt;00000f0: 0300 0100 0900 0000 0300 0000 0100 0000  ................&lt;br /&gt;0000100: 0100 0000 0000 0000 ffff ffff 0000 0000  ................&lt;br /&gt;0000110: e101 0000 0000 0000 0100 0100 0100 0000  ................&lt;br /&gt;0000120: 0000 0000 0400 0000 7010 0000 0000 0e00  ........p.......&lt;br /&gt;0000130: 0300 0200 0200 0000 0000 0000 0900 0000  ................&lt;br /&gt;0000140: 6f20 0100 2100 1500 037f 6e20 0400 0100  o ..!.....n ....&lt;br /&gt;0000150: 0e00 0000 0100 0000 0000 0000 0100 0000  ................&lt;br /&gt;0000160: 0200 063c 696e 6974 3e00 0149 0016 4c61  ...&lt;init&gt;..I..La&lt;br /&gt;0000170: 6e64 726f 6964 2f61 7070 2f41 6374 6976  ndroid/app/Activ&lt;br /&gt;0000180: 6974 793b 0013 4c61 6e64 726f 6964 2f6f  ity;..Landroid/o&lt;br /&gt;0000190: 732f 4275 6e64 6c65 3b00 204c 636f 6d2f  s/Bundle;. Lcom/&lt;br /&gt;00001a0: 616e 6472 6f69 642f 6865 6c6c 6f2f 4865  android/hello/He&lt;br /&gt;00001b0: 6c6c 6f41 6e64 726f 6964 3b00 0156 0002  lloAndroid;..V..&lt;br /&gt;00001c0: 5649 0002 564c 0008 6f6e 4372 6561 7465  VI..VL..onCreate&lt;br /&gt;00001d0: 000e 7365 7443 6f6e 7465 6e74 5669 6577  ..setContentView&lt;br /&gt;00001e0: 0000 0001 0102 8180 0498 0203 01b0 0200  ................&lt;br /&gt;00001f0: 0b00 0000 0000 0000 0100 0000 0000 0000  ................&lt;br /&gt;0000200: 0100 0000 0a00 0000 7000 0000 0200 0000  ........p.......&lt;br /&gt;0000210: 0500 0000 9800 0000 0300 0000 0300 0000  ................&lt;br /&gt;0000220: ac00 0000 0500 0000 0500 0000 d000 0000  ................&lt;br /&gt;0000230: 0600 0000 0100 0000 f800 0000 0120 0000  ............. ..&lt;br /&gt;0000240: 0200 0000 1801 0000 0110 0000 0200 0000  ................&lt;br /&gt;0000250: 5401 0000 0220 0000 0a00 0000 6201 0000  T.... ......b...&lt;br /&gt;0000260: 0020 0000 0100 0000 e101 0000 0010 0000  . ..............&lt;br /&gt;0000270: 0100 0000 f001 0000                      ........&lt;br /&gt;&lt;/pre&gt;Only the 4 16-bit words at 0x128 and the 9 16-bit words at 0x140 are actual Dalvik bytecode. They belong to the constructor and the onCreate method respectively.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-2965692667949445980?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/2965692667949445980/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=2965692667949445980' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2965692667949445980'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2965692667949445980'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2009/02/minimal-dalvik-executables_06.html' title='Minimal Dalvik Executables'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-2340865635045721023</id><published>2009-01-22T20:10:00.001-08:00</published><updated>2011-04-06T01:49:02.382-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Developing Android without Eclipse or Ant</title><content type='html'>&lt;a href="http://code.google.com/android/intro/develop-and-debug.html"&gt;The Android documentation recommends using Eclipse for development&lt;/a&gt;, but this is sluggish on my laptop, and feels cluttered on compact screens. A stated alternative is to use Ant, but this is also unnecessary as the build commands it invokes can be easily extracted and executed manually. This suits my preference for a minimalistic or practically non-existent IDE, especially on laptops, as well as my distaste for editing XML. As a bonus, on Ubuntu, I install one package:&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;$ sudo apt-get install sun-java6-jdk&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;along with the Android SDK and I'm ready to develop for Android. In contrast, the Ant package has several dependencies, and the required version of Eclipse is newer than the currently available Ubuntu package.&lt;br /&gt;&lt;br /&gt;Here are the details:&lt;br /&gt;&lt;tt&gt;&lt;br /&gt;$ SDKDIR=~/android-sdk-linux_x86-1.0_r2  # Where I extracted the SDK.&lt;br /&gt;$ PATH=$SDKDIR/tools:$PATH&lt;br /&gt;$ activitycreator -o somepath some.project.name&lt;br /&gt;$ cd somepath&lt;br /&gt;&lt;/tt&gt;&lt;br /&gt;At this point Ant can build the project, but masochists like me will prefer to shun Ant and run the underlying commands themselves. The following command generates &lt;tt&gt;R.java&lt;/tt&gt; from the resources:&lt;br /&gt;&lt;tt&gt;&lt;br /&gt;$ aapt p -m -J src -M AndroidManifest.xml -S res -I $SDKDIR/android.jar&lt;br /&gt;&lt;/tt&gt;&lt;br /&gt;Next, the source is compiled to &lt;tt&gt;.class&lt;/tt&gt; files. This is why we need the JDK:&lt;br /&gt;&lt;tt&gt;&lt;br /&gt;$ mkdir bin/classes&lt;br /&gt;$ javac -encoding ascii -target 1.5 -d bin/classes \&lt;br /&gt;  -bootclasspath $SDKDIR/android.jar src/some/project/*&lt;br /&gt;  # When libs exist, append -classpath=libs/*.jar&lt;br /&gt;&lt;/tt&gt;&lt;br /&gt;These files contain instructions for the JVM. They are converted to Dalvik bytecode via:&lt;br /&gt;&lt;tt&gt;&lt;br /&gt;$ dx --dex --output=bin/classes.dex bin/classes  # If libs exist, append libs/*.jar&lt;br /&gt;&lt;/tt&gt;&lt;br /&gt;The resources and assets are packaged:&lt;br /&gt;&lt;tt&gt;&lt;br /&gt;$ aapt p -f -M AndroidManifest.xml -S res -I $SDKDIR/android.jar -F bin/projectname.ap_&lt;br /&gt;  #  When assets exist, add -A assets&lt;br /&gt;&lt;/tt&gt;&lt;br /&gt;This package is itself packaged with the Dalvik bytecode to make the final product:&lt;br /&gt;&lt;tt&gt;&lt;br /&gt;$ apkbuilder bin/something.apk -z bin/projectname.ap_ -f bin/classes.dex -rf src -rj libs&lt;br /&gt;  # Use -u for unsigned builds.&lt;br /&gt;&lt;/tt&gt;&lt;br /&gt;Now if I could only roll my own Dalvik compiler for a language I like, I could avoid the Java SDK, as well as Java itself.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-2340865635045721023?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/2340865635045721023/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=2340865635045721023' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2340865635045721023'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2340865635045721023'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2009/01/developing-android-without-eclipse-or_22.html' title='Developing Android without Eclipse or Ant'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-6030312230834377279</id><published>2009-01-04T15:54:00.001-08:00</published><updated>2011-04-06T01:49:02.391-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Encoding videos for the G1</title><content type='html'>Soon after Android's debut, initial reviews were complaining about the G1's inability to play videos. At the time I felt it was a trifling matter. Since YouTube already works on the G1, a more general video player ought to require only minor tinkering. And why is it a big deal anyway? Are people in a rush to experience Hollywood blockbusters on a tiny screen and tinny speakers?&lt;br /&gt;&lt;br /&gt;Sure enough, video playing apps appeared quickly on Android Market. I found at least three in my last search. As for their utility: now that I've played with them, I understand why some see a video player as a must-have, and not merely yet another vehicle for demonstrating the G1's capabilities.&lt;br /&gt;&lt;br /&gt;While breathtaking cinematography, award-winning soundtracks and state-of-the-art special effects translate poorly to a handheld device, movies that derive their strength chiefly from factors such as a witty dialogue lose little. Informational clips are another good use case. And even with scenes that only work well with a big screen: if they're my favourite scenes of all time, it's still comforting to have them at my fingertips, even if they are greatly reduced.&lt;br /&gt;&lt;br /&gt;Which leaves the problem of converting videos to a G1-friendly format. I knew almost nothing about the black art of video encoding, but after hours of false starts and trawling the net for help, I still know almost nothing. However, I picked up enough to get started.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://android-dls.com/wiki/index.php?title=Android_FAQ#Q:_What_multimedia_.28audo.2Fvideo.29_codecs_does_the_G1_support.3F"&gt;An unofficial FAQ&lt;/a&gt; states the G1 currently supports the H.264, 3GP and MPEG4 video codecs. I skimmed a few Wikipedia entries to decode this alphanumeric soup, and my interpretation is that this really means the G1 recognizes .MP4 and .3GP files (which are container formats and not video encoding methods) and understands the H.263, MPEG-4 ASP ("roughly similar" to H.263) and H.264 (aka MPEG-4 AVC) video compression standards. [Trivia: ASP expands to the somewhat oxymoronic "Advanced Simple Profile".]&lt;br /&gt;&lt;br /&gt;The G1 only speaks &lt;a href="http://en.wikipedia.org/wiki/H.264#Profiles"&gt;the BP (Baseline Profile) dialect of H.264&lt;/a&gt;; videos that use more advanced features of H.264 play incorrectly, or not at all. [&lt;a href="http://blogs.amd.com/patmoorhead/archive/2008/10/27/unlock-some-secret-of-the-android-g1-video-encode-decode.aspx"&gt;At last this post makes sense to me&lt;/a&gt;!]&lt;br /&gt;&lt;br /&gt;On Ubuntu, the easiest way to encode videos was to use avidemux (sudo apt-get install avidemux). For audio, I only tested the AAC codec (usually at 96kbps), but several others should also work. For video, selecting MPEG-4 ASP produced videos that seemed to tax the CPU heavily on playback, often to the point of unwatchability. Perhaps this could be remedied by lowering the number of frames per second via an appropriate filter.&lt;br /&gt;&lt;br /&gt;In any case, I prefer selecting the MPEG-4 AVC video codec and dumbing it down for the G1 by disabling the appropriate &lt;a href="http://avidemux.org/admWiki/index.php?title=H264"&gt;H.264 options&lt;/a&gt;: I changed the Max Consecutive B-Frames to 0, unchecked all options related to B-frames, and disabled CABAC along with the 8x8 Transform and 8x8 Intra search options. As before, I added the MPlayer variant of the resize filter: the G1 has a 480x320 screen in landscape mode. At 384kbps, this produced videos that played smoothly though higher bit rates should be feasible.&lt;br /&gt;&lt;br /&gt;Now that I appreciate video more, I eagerly await the "Cupcake" Android update which is rumored to have video recording. I also hope the next generation of hardware will have video out and enough power to handle any popular codec.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-6030312230834377279?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/6030312230834377279/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=6030312230834377279' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/6030312230834377279'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/6030312230834377279'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2009/01/encoding-videos-for-g1_04.html' title='Encoding videos for the G1'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-5261655868524260455</id><published>2009-01-03T04:35:00.001-08:00</published><updated>2011-04-06T01:49:02.402-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><title type='text'>G1 Miscellany</title><content type='html'>What fortuitous timing! Intending to start from a clean slate, I performed a factory reset on my G1 recently. I had forgotten that, unlike &lt;a href="http://code.google.com/android/dev-devices.html"&gt;the Developer Edition of the G1&lt;/a&gt;, even an unlocked standard issue G1 requires a SIM card with a data plan for the initial setup.&lt;br /&gt;&lt;br /&gt;I was going to wait until I could borrow a suitable SIM from a friend for a few seconds (as after registration, I'm happy to use just WiFi for my G1 net needs for the time being), but I just heard about &lt;a href="http://forum.xda-developers.com/showthread.php?t=442480"&gt;a new jailbreak for all G1s&lt;/a&gt;. It works by flashing the G1 back to RC29 where the old jailbreak can be exploited. Once done, one can &lt;a href="http://forum.xda-developers.com/showthread.php?t=452316"&gt;register the G1 via WiFi&lt;/a&gt;, without a data plan or even a SIM.&lt;br /&gt;&lt;br /&gt;Some Linux-specific notes. The G1 is touchy about the SD card for the RC29 image. The time it worked for me, I had reformatted it with "mkfs -t vfat /dev/foo", and used uppercase exclusively for the filename. To get the "adb" tool from &lt;a href="http://code.google.com/android/download.html"&gt;the Android SDK&lt;/a&gt; working, I had to &lt;a href="http://code.google.com/android/intro/develop-and-debug.html"&gt;create an appropriate /etc configuration file&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I only needed two of the incantations. On the G1, I enabled adb via:&lt;br /&gt;&lt;tt&gt;&amp;lt;enter&amp;gt;setprop persist.service.adb.enable 1&amp;lt;enter&amp;gt;&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;and once in the adb shell, I opened the Settings menu before registration:&lt;br /&gt;&lt;tt&gt;$ am start -a android.intent.action.MAIN -n com.android.settings/.Settings&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;From here it was trivial to setup WiFi and sign in.&lt;br /&gt;&lt;br /&gt;Some unrelated tips. When typing, Alt + Delete clears the whole line. &lt;a href="http://www.remotedroid.net/"&gt;RemoteDroid turns the G1 into a wireless keyboard and mouse&lt;/a&gt;; a fun application to show off.&lt;br /&gt;&lt;br /&gt;I remain easily amused by the digital compass. The Orienteer app is often the first app I demonstrate. Also, the Sky Map app is perfect for those interested in picking out constellations in the night sky, but not interested enough to actually memorize them and figure out when and where they can be seen.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-5261655868524260455?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/5261655868524260455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=5261655868524260455' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5261655868524260455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5261655868524260455'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2009/01/g1-miscellany_03.html' title='G1 Miscellany'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-1395149946897088833</id><published>2008-11-26T17:52:00.001-08:00</published><updated>2011-04-06T01:49:02.406-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>The T-Mobile G1</title><content type='html'>I had intended to code more for my &lt;a href="http://benlynn.blogspot.com/2008/03/gp2x-revisited.html"&gt;GP2X&lt;/a&gt;, starting with tweaking my version of Netwalk. But I almost certainly never will, now that I recently received a T-Mobile G1 phone. Though less suitable for gaming, its smaller size along with its phone and WiFi mean I'll actually carry it everywhere and run its apps.&lt;br /&gt;&lt;br /&gt;I had thought it likely that I'd end up reverting to my old simple phone and only access the Internet with my laptop, as I have always done, but the G1 has enough right that I finally see why so many people are effectively surgically grafted to their smartphones. Judging by the last few days, I've become one of those addicts I formerly mocked. I've even caught myself scrolling the home view (or whatever it's called) back and forth, just to enjoy the smooth parallax action at my fingertips.&lt;br /&gt;&lt;br /&gt;Even now I get that "shiny new toy" delight from discovering a new G1 trick. Some "Wow!"-worthy free apps:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/twisty/"&gt;Twisty&lt;/a&gt;: Interactive fiction on the go. (Not on Android Market at the moment; I installed this by allowing unknown sources and running ApkInstaller.)&lt;/li&gt;&lt;li&gt;ShopSavvy, CompareEverywhere: When did you last own a phone that could scan barcodes for price comparisons?&lt;/li&gt;&lt;li&gt;WikiTude: Try this futuristic app in a photogenic location.&lt;/li&gt;&lt;li&gt;StreetView: Digital-compass-enhanced deja vu on demand.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Amazed: Hone your tilting skills.&lt;/li&gt;&lt;li&gt;Voice Dialer: I have to put on an American accent to get digits working, but I'm impressed it recognizes my atrocious pronunciation of certain names. When will we see more voice control and text-to-speech apps?&lt;/li&gt;&lt;li&gt;Flashlight: Unexpectedly handy. If only I could find a colour that brings out the &lt;a href="http://www.eff.org/issues/printers"&gt;printer dot codes&lt;/a&gt;. Or even better, an app that could &lt;a href="http://w2.eff.org/Privacy/printers/docucolor/"&gt;decode the patterns&lt;/a&gt; for the secret agent appeal.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Full disclosure: I work for Google, though not on Android. Not that it matters as you should approach everything you read with healthy skepticism! Anyway, I'm not completely happy with the G1...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The &lt;/span&gt;&lt;i style="font-weight: bold;"&gt;n&lt;/i&gt;&lt;span style="font-weight: bold;"&gt; things I hate about Java&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;If it weren't for Java, I'd already be writing Android applications instead of writing about them. I've never gotten along well with the language. I'm old enough to remember the hype surrounding its release. I instinctively distrust excessive marketing (which indirectly caused an unrelated language to be named "JavaScript") and my experiences have only reinforced my initial disdain.&lt;br /&gt;&lt;br /&gt;When I first examined Java, I was dismayed that autoboxing was unsupported (&lt;a href="http://java.sun.com/j2se/1.5.0/docs/guide/language/autoboxing.html"&gt;now fixed&lt;/a&gt;) resulting in the "int versus Integer" mess. I also felt they had missed a golden opportunity to leave the most confusing parts of C syntax behind, instead of superficially pandering to veteran programmers. Also, my pro-multiple-inheritance stance obviously clashed with Java. It seemed to me Java was made public before it was ready. Resources spent on marketing may have been better spent on improving the language before release.&lt;br /&gt;&lt;br /&gt;Years later, another encounter cemented my opinion. In a project where I had to use Java, I wanted a thread to perform a nonblocking read on a channel. I was mildly perturbed when I found the Java threading API describing certain functions as deprecated because they were impossible to implement safely. How could they get this wrong? Surely they could have studied any of the many previous threading APIs?&lt;br /&gt;&lt;br /&gt;More distressing was my lengthy trial-and-error discovery that my desired nonblocking read was impossible, due to some API design detail which I've gladly forgotten. I do remember documentation claiming the issue was addressed in a newer version, but unfortunately I was constrained to an older version. When I eventually worked around the problem, I felt especially frustrated since I could do exactly what I wanted in a few lines of C.&lt;br /&gt;&lt;br /&gt;Shifts in my beliefs have further lessened Java's appeal. I was once an object-oriented fanatic, even submitting an object-oriented entry to &lt;a href="http://www.cs.cornell.edu/icfp/"&gt;a programming contest&lt;/a&gt; in a futile attempt to "prove" it was the right way to design software. (To clarify, I'm not saying the attempt was futile because it was object-oriented; the blame lies with me.) In those days I shunned Java because it wasn't object-oriented enough. Since I renounced my object-oriented ways years ago, I now feel Java is too object-oriented!&lt;br /&gt;&lt;br /&gt;Now, to &lt;a href="http://code.google.com/android/"&gt;develop for Android&lt;/a&gt;, I must face Java again. That I'm willing to consider this is itself a glowing endorsement of the G1. I remain unimpressed: Java's primary advantage appears to be its fame, not its features. In fact, developers are urged to &lt;a href="http://code.google.com/android/toolbox/performance.html"&gt;avoid common object-oriented practices for performance reasons&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Additionally, the downloads are heavyweight; a good net connection is a must. The &lt;a href="http://code.google.com/android/intro/hello-android.html"&gt;"Hello, Android"&lt;/a&gt; example somehow requires a nontrivial amount of code as well as an XML file. In contrast, for C, &lt;a href="http://bellard.org/otcc/otcc.c"&gt;a kinda sorta C compiler fits in a few hundred lines&lt;/a&gt;, and "Hello, World" fits right here:&lt;span style="font-weight: bold;"&gt; int puts(const char *);int main(){return puts("Hello, World!");}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This comparison is grossly unfair, as C libraries for such a sophisticated phone would also be nontrivial, but all the same, if the Bell Labs researchers of yore were behind the drawing board, the programming interface would be terse and elegant.&lt;br /&gt;&lt;br /&gt;One bright spot is Android's register-based &lt;a href="http://sites.google.com/site/io/dalvik-vm-internals"&gt;Dalvik virtual machine&lt;/a&gt;. A prevailing attitude was (and still is?) that Java could replace C/C++ in most situations: how can this be when the standard JVM is a stack-based snail? This is yet another detail Java should have got right from the start; compare with &lt;a href="http://doc.cat-v.org/inferno/4th_edition/dis_VM_design"&gt;the design of Inferno's Dis virtual machine&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I hope Dalvik bytecode compilers for more agreeable languages will spring forth soon, as that will eliminate my biggest gripe.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Hardware Hankerings&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Some deficiencies can be resolved with a little money. At the top of my wishlist is a standard stereo jack so I can use it as a stand-alone music player; I'll probably get an &lt;a href="http://www.google.com/search?hl=en&amp;amp;q=htc%203%20in%201"&gt;HTC 3-in-1 USB adapter&lt;/a&gt; for this purpose. (New G1 buyers will get an adapter free, but that's little consolation for me.) I'll probably also fork over dough for a bigger micro SDHC card, preferably at least 8GB so I can laugh at standard 4.7GB DVDs.&lt;br /&gt;&lt;br /&gt;Other features will have to wait for the next generation. For gaming, I'd love to have an Android device with a nice D-pad or two; see &lt;a href="http://www.openpandora.org/"&gt;the Pandora&lt;/a&gt;. While I'm at it, a video camera as well. One that can face the user so you could videophone others like in many a sci-fi flick. And then I may as well ask for a video out too, so it could be a stand-alone video player. Rather than build in all these gadgets, it may be better to have a few standard ports (perhaps USB) to attach oddball devices. I can't wait to see what they'll come up with.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-1395149946897088833?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/1395149946897088833/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=1395149946897088833' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1395149946897088833'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1395149946897088833'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2008/11/t-mobile-g1_26.html' title='The T-Mobile G1'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-7177566367392003506</id><published>2008-11-05T16:35:00.001-08:00</published><updated>2011-04-06T01:49:02.410-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Notes on my notes on Git</title><content type='html'>I was mildly shocked, but honoured, to find that the venerable (or at least popular!) Wikipedia references my tutorial in &lt;a href="http://en.wikipedia.org/wiki/Git_(software)"&gt;its article on Git&lt;/a&gt;. Interestingly, the entry states that Git tips and tricks are 'popularly referred to as "magic"'. I named my guide &lt;a href="http://www-cs-students.stanford.edu/~blynn/gitmagic/"&gt;Git Magic&lt;/a&gt; as a homage to Arthur C. Clarke's quip featured in the preface, and also because magic happens to be a hobby of mine. If the Wikipedia comment is true, then either my title was fortuitously appropriate, or I have unwittingly coined a phrase.&lt;br /&gt;&lt;br /&gt;More pleasant surprises followed. The guide appeared on the first page when I Googled for "git" (though sadly much lower on Yahoo). I found several pages linking to mine, whose recommendations were probably responsible. Thanks to everyone who praised and promoted my site. [I do work for Google, but I have no control over the ranking of my content.]&lt;br /&gt;&lt;br /&gt;It almost makes me feel guilty that my enthusiasm for maintaining the guide and for source control in general has waned. But this is inevitable because of Git's power and convenience: I use Git so frequently that it has faded into the background. Fascinating new commands have gradually transformed into boring old reflexes. Documenting them has as much appeal and utility to me as documenting my exploits with, say, the &lt;tt&gt;ls&lt;/tt&gt; command, or sleeping and eating. Also, there are many more Git tutorials these days. But as long as my site ranks well, I'll attempt to keep it updated.&lt;br /&gt;&lt;br /&gt;I use Google Analytics to keep an eye on site traffic as I lack access to the logs on the Stanford server hosting Git Magic. Some observations:&lt;br /&gt;&lt;br /&gt;On 20080412, traffic to my site, which includes Git Magic, spiked at over 10000 hits. Previously few days saw more than 100 hits. The spike rapidly diminished to 100 to 200 hits per day until 20080805, when a larger spike of 16000 hits occurred. Again, this activity tapered off quickly to a few hundred hits per day until 20081102, which featured a small spike of over 3500 hits.&lt;br /&gt;&lt;br /&gt;It seems the first and third spikes were caused by &lt;a href="http://reddit.com/"&gt;reddit.com&lt;/a&gt;, and the second spike was caused by an article at &lt;a href="http://lwn.net/"&gt;LWN.net&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Since mid-September 2007, roughly when the guide was released, the top three browsers were Firefox (67.43%), Safari (12.48%) and Internet Explorer (6%). The top three operating systems were Windows (36.53%), Linux (35.15%) and Macintosh (26.9%).&lt;br /&gt;&lt;br /&gt;A handful of hits identified themselves as mobile devices: iPhone (489), iPod (113), SymbianOS (19), DangerHiptop (11), PalmOS (4), Android (3). Some intriguing curiosities: Playstation 3 (8), Nintendo Wii (6) and OS/2 (1).&lt;br /&gt;&lt;br /&gt;During the last spike, the top three browsers were Firefox (69.42%), Safari (12.33%) and Chrome (5.15%), and the top three operating systems were Windows (49.45%), Macintosh (27.23%) and Linux (21.73%).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-7177566367392003506?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/7177566367392003506/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=7177566367392003506' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7177566367392003506'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7177566367392003506'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2008/11/notes-on-my-notes-on-git_05.html' title='Notes on my notes on Git'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-6893920307666113105</id><published>2008-11-05T02:09:00.001-08:00</published><updated>2011-04-06T01:49:02.414-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><title type='text'>Restoring GRUB after reinstalling XP</title><content type='html'>For me, a common system setup is dual-boot Windows and Linux, and a common problem is an overwritten MBR (Master Boot Record) after reinstalling Windows XP, making it impossible to  boot to Linux. Most sites recommend using a Linux install or rescue CD to fix this, but I prefer to install the GRUB bootloader on the Windows partition, and then use GRUB to boot into Linux to repair the boot record:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Download the latest zip file from &lt;a href="https://gna.org/projects/grub4dos/"&gt;GRUB for DOS&lt;/a&gt; and extract the file &lt;span style="font-family:courier new;"&gt;grldr&lt;/span&gt; to C:\&lt;/li&gt;&lt;li&gt;Run &lt;tt&gt;cmd&lt;/tt&gt; and type:&lt;br /&gt;&lt;tt&gt;cd \&lt;br /&gt;attrib +r +s +h boot.ini&lt;br /&gt;edit boot.ini&lt;/tt&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Append the line&lt;br /&gt;&lt;tt&gt;c:\grldr="GRUB"&lt;/tt&gt;&lt;br /&gt;to the &lt;tt&gt;operating systems&lt;/tt&gt; section, which is usually last. Save and quit.&lt;/li&gt;&lt;li&gt;Type&lt;br /&gt;&lt;tt&gt;attrib -r -s -h boot.ini&lt;/tt&gt;&lt;/li&gt;&lt;li&gt;Reboot and select GRUB from the boot menu.&lt;/li&gt;&lt;li&gt;From the grub prompt we can repair the system:&lt;br /&gt;&lt;tt&gt;find /boot/grub/stage1&lt;br /&gt;root (hd&lt;i&gt;X&lt;/i&gt;, &lt;i&gt;Y&lt;/i&gt;)&lt;br /&gt;setup (hd0)&lt;br /&gt;reboot&lt;/tt&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Take care with &lt;tt&gt;boot.ini&lt;/tt&gt; as invalidating this file can render the system unbootable. In this case you must boot by another method, e.g. network, USB, CD or floppy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-6893920307666113105?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/6893920307666113105/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=6893920307666113105' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/6893920307666113105'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/6893920307666113105'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2008/11/restoring-grub-after-reinstalling-xp_05.html' title='Restoring GRUB after reinstalling XP'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-4424479448069266902</id><published>2008-09-04T21:04:00.001-07:00</published><updated>2011-04-06T01:49:02.416-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>AsciiDoc and MathML</title><content type='html'>Nowadays I use &lt;a href="http://www.methods.co.nz/asciidoc/userguide.html"&gt;AsciiDoc&lt;/a&gt; to author HTML. I'm even converting old pages to use this convenient and presentable text format.&lt;br /&gt;&lt;br /&gt;My &lt;a href="http://crypto.stanford.edu/pbc/notes/"&gt;mathematics notes&lt;/a&gt; present an interesting challenge. For MathML the AsciiDoc user guide recommends using double-dollar passthroughs around equations, but this implies at least three characters are required to delimit every equation, since &lt;a href="http://www1.chapman.edu/%7Ejipsen/mathml/asciimath.html"&gt;ASCIIMathML&lt;/a&gt; or equivalent itself needs at least one character.&lt;br /&gt;&lt;br /&gt;I'd like to have something like:&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-family:courier new;"&gt;= Introduction =&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Let $E: Y^2 = X^3 + a X + b$ be an elliptic curve.&lt;/span&gt;&lt;/blockquote&gt;to just work, so I settled on the following solution. I use &lt;a href="http://pear.math.pitt.edu/mathzilla/itex2mml.html"&gt;itex2MML&lt;/a&gt; because&lt;br /&gt;&lt;ul&gt;&lt;li&gt;My old-fashioned, superstitious, purist side prefers bare-bones JavaScript-free static documents.&lt;/li&gt;&lt;li&gt;Familiarity.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It has a handy syntax for equations in display mode, i.e. equations in their own center-justified paragraphs as seen in mathematics texts. An invaluable feature, as I can't figure out how to center a paragraph with AsciiDoc.&lt;/li&gt;&lt;/ul&gt;It should be simple to substitute an alternative such as ASCIIMathML. Some may prefer its approach because documents are smaller, and the page source is intelligible. But I argue that most never bother viewing the source, the savings are slight, and one can always provide the AsciiDoc source if these factors matter.&lt;br /&gt;&lt;br /&gt;Write these rules to a file named "macros":&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;/span&gt;&lt;blockquote&gt;&lt;span style="font-family:courier new;"&gt;[miscellaneous]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;newline=\n&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;[blockdef-passthrough]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;delimiter=^@{4,}$&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;subs=none&lt;/span&gt;&lt;br /&gt;&lt;/blockquote&gt;The first is optional. I just abhor the CR LF abomination.&lt;br /&gt;&lt;br /&gt;Then feed the source through these commands to produce the final product, which should be a file with an .xhtml extension:&lt;br /&gt;&lt;blockquote style="font-family: courier new;"&gt;sed 's/\$\([^$]*\)\$/+++$\1$+++/g' \&lt;br /&gt;| sed '/\\\[/i@@@@' \&lt;br /&gt;| sed '/\\\]/a@@@@' \&lt;br /&gt;| asciidoc -b xhtml11 -f macros  - \&lt;br /&gt;| itex2MML | sed '/&amp;lt;!DOCTYPE/c \&lt;br /&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN"\&lt;br /&gt;"http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd" [\&lt;br /&gt; &amp;lt;!ENTITY mathml "http://www.w3.org/1998/Math/MathML"&amp;gt; ]&amp;gt;&lt;br /&gt;/xhtml11.dtd/d'&lt;/blockquote&gt;The above gobbledygook [careful with some of those newlines; Blogger may have put extra cuts in my lines] performs the following:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Put the "+++" AsciiDoc inline passthrough around equations delimited by "$".&lt;/li&gt;&lt;li&gt;Surround display-mode equations, delimited by "\[" and "\]", with our newly defined "@@@@" block passthrough macro.&lt;/li&gt;&lt;li&gt;Put the resulting mess through AsciiDoc, which converts everything but our equations to an HTML 1.1 document.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Run itex2MML to convert the equations to MathML.&lt;/li&gt;&lt;li&gt;Fix the DOCTYPE declaration.&lt;/li&gt;&lt;/ol&gt;Caveats:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Inline equations must be closed on the line they are opened.&lt;/li&gt;&lt;li&gt;Expressions such as "$i$th" must be written as "$i$#th#", since AsciiDoc "+++" quotes are constrained.&lt;/li&gt;&lt;/ul&gt;I'm content with this setup, but I'm considering extending my script to detect equations automatically, a recent feature of ASCIIMathML, so that even the dollar signs are unnecessary.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-4424479448069266902?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/4424479448069266902/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=4424479448069266902' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4424479448069266902'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4424479448069266902'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2008/09/asciidoc-and-mathml_04.html' title='AsciiDoc and MathML'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-9165440414682222495</id><published>2008-06-20T23:22:00.002-07:00</published><updated>2011-04-06T01:49:02.420-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Memory Mapped Files</title><content type='html'>I've only recently learned about &lt;a href="http://en.wikipedia.org/wiki/Memory-mapped_file"&gt;memory-mapped files&lt;/a&gt; in Linux. Until recently, to perform file I/O, I followed the textbook examples of using read and write calls on file descriptors or streams. It wasn't until I browsed the &lt;a href="http://git.or.cz/"&gt;git source&lt;/a&gt; (to help with &lt;a href="http://www-cs-students.stanford.edu/%7Eblynn/gg/"&gt;my git clone&lt;/a&gt;) that I came across a mysterious mmap() call.&lt;br /&gt;&lt;br /&gt;Not only is it more efficient for the reasons given in the Wikipedia article, but it is easier to work with file contents as if they were already in memory. There's no need to learn the syntax and semantics of various read and write calls.&lt;br /&gt;&lt;br /&gt;The only tough part is remembering the mmap syntax. But the call can be readily encapsulated. For example, to read a file, I define a function like the following:&lt;br /&gt;&lt;pre&gt;&lt;span style="font-family:courier new;"&gt;void *map_file_to_memory(size_t *len, const char *path) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  int fd = open_or_die(path, O_RDONLY);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  *len = get_size_of(fd);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  if (!*len) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    close(fd);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    return NULL;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  void *map = mmap(NULL, *len, PROT_READ, MAP_PRIVATE, fd, 0);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  if (MAP_FAILED == map) die("mmap failed");&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  close(fd);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  return map;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;/pre&gt;There are concerns with large files, but hopefully madvise() is well-written and can prevent excessive page faults.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-9165440414682222495?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/9165440414682222495/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=9165440414682222495' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/9165440414682222495'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/9165440414682222495'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2008/06/memory-mapped-files_20.html' title='Memory Mapped Files'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-8389990052101224415</id><published>2008-05-16T01:05:00.001-07:00</published><updated>2011-04-06T01:49:02.423-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='games'/><title type='text'>Thai Rummy</title><content type='html'>I've already written about &lt;a href="http://benlynn.blogspot.com/2007/02/chase-pig.html"&gt;my fascination with playing cards and card games&lt;/a&gt;. At last I've filled in a conspicuous gap in my repertoire: a two-player card game I can play for hours.&lt;br /&gt;&lt;br /&gt;I've somehow overlooked a family of two-player card games that require only a standard deck and are playable in many situations: Gin Rummy and friends. I had tried playing before but I never appreciated them until recently, when a veteran taught me an interesting variant, and repeatedly beat me easily. At first, that is!&lt;br /&gt;&lt;br /&gt;Apparently, this variant is popular in Thailand, but I have yet to see these precise rules described elsewhere, though &lt;a href="http://en.wikipedia.org/wiki/500_Rum"&gt;500 Rum&lt;/a&gt; comes close.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Deal&lt;/span&gt;: I believe the first dealer is determined randomly. In following hands, the loser of the previous hand is dealer.&lt;br /&gt;&lt;br /&gt;The dealer deals 13 cards to each player, and places the remainder, the stock, face-down. The top card of the stock is placed face-up in between the players to start the discard pile. The non-dealer goes first.&lt;br /&gt;&lt;br /&gt;One aims to meld all cards in hand, either in sets: 3 or 4 cards of the same rank, or in runs: 3 or more cards of the same suit in sequence. A card can only be melded once, so it is impossible for it to count towards a set and a sequence. Aces are always high, and 2s are always low, so in particular A-2-3 is an invalid run.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Play&lt;/span&gt;: Each turn consists of three phases:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1. Draw&lt;/span&gt;: a player can take a card within the discard pile, if they can use it to form a set or run with at least one card in their hand and optionally using discards further up in the pile, and additionally if they take every card lying on top. They must display the meld, by laying it on the table in front of them.&lt;br /&gt;&lt;br /&gt;In other words, they take some number of cards from the top of the discard pile, and use the bottommost card taken in a meld, which must then be exposed immediately.&lt;br /&gt;&lt;br /&gt;If a player is unwilling or unable to take any cards from the discard pile, they must draw a card from the stock, unless the stock is exhausted in which case the player must pass. If both players pass, the hand ends.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2. Meld&lt;/span&gt;: the player may display on the table as many melds as desired from their hand, so long as they have at least one card left in hand. They may add cards to existing melds belonging to either player, but in both cases the cards are placed in front of the player extending the meld.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;3. Discard&lt;/span&gt;: the player must discard one card. If it is their last card because all their other cards have been displayed, then it is placed face-down on the discard pile and the hand ends. Otherwise it is placed face-up on top of the discard pile and off to one side, so that every card in the discard pile can be seen at all times.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Scoring&lt;/span&gt;: A player receives 50 points for placing a card face-down on the discard pile, that is, when they have melded all their cards save one which becomes the discard. They are considered the winner of the hand, even if they ultimately have a lower score. If the game ends but both players still have cards, the winner is the player with the higher score.&lt;br /&gt;&lt;br /&gt;A player adds the values of the cards displayed in front of them and subtracts the values of the cards remaining in their hand: cards of rank 2 to 9 are worth 5 points each, 10, J, Q, K are worth 10 points each, and Aces are 15 points each, with two exceptions. Both the Queen of Spades and Two of Clubs are worth 50 points.&lt;br /&gt;&lt;br /&gt;Typically a hand ends because one player has melded their entire hand, and only one player subtracts the values of cards from their total. Both players subtract values of cards only when a hand ends because both players have passed.&lt;br /&gt;&lt;br /&gt;There are two "stupidity" penalties. Firstly, if a player wins by melding their whole hand after taking just the top card of the discard pile, that is, the opponent's last discard, the opponent subtracts 50 points from their score.&lt;br /&gt;&lt;br /&gt;Secondly, during the game, when a player discards a card that could have been used to extend an exposed meld, that player loses 50 points. The penalty is cumulative; each offence costs a player 50 points.&lt;br /&gt;&lt;br /&gt;Lastly, there is a doubling rule: if a player does not display any melds except possibly in the final turn, their score for the hand is doubled. So if a player wins by melding their entire hand, and had not previously displayed any melds, their score is doubled. This is almost always beneficial because they usually have a positive score. And if a player wins by melding their entire hand and their opponent has not yet displayed a single meld, the opponent's score is doubled. This is always harmful to the opponent because they must have a negative score, seeing as no melds were made and cards remain in hand.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Winning&lt;/span&gt;: The final score is added to a running total. There are two victory conditions. If a player has -500 or fewer points, their opponent wins the game instantly. Otherwise the first player to have 500 points or more after going out wins the game.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;More players&lt;/span&gt;: if there are a few more players, deal three fewer cards per extra player.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Distinguishing Characteristics&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Summary for those familiar with rummy games:&lt;br /&gt;&lt;ul&gt;&lt;li&gt; Aces always high, 2s always low&lt;/li&gt;&lt;li&gt; Can take variable number of cards from top of discard pile; must use the bottommost card taken&lt;/li&gt;&lt;li&gt; Must make a new run or set with this card; cannot just extend existing meld&lt;/li&gt;&lt;li&gt; Must meld with at least one card in hand with this card&lt;/li&gt;&lt;li&gt; Boathouse rule: must discard when going out&lt;/li&gt;&lt;li&gt;Hand ends immediately after a player goes out&lt;/li&gt;&lt;li&gt;50 pts for going gin&lt;/li&gt;&lt;li&gt;-50 pts for giving opponent single discard used to go out&lt;/li&gt;&lt;li&gt;-50 pts for discard card that could augment a displayed meld&lt;/li&gt;&lt;li&gt; Score doubled for never displaying melds&lt;/li&gt;&lt;li&gt; Values of cards: 2-9: 5 pts, 10-K: 10 pts, A: 15, except QS, 2C: 50 pts.&lt;/li&gt;&lt;li&gt;-500 pts loses the game&lt;/li&gt;&lt;li&gt;500 pts insufficient for win; must also go out&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Update 20090504&lt;/span&gt;: Ricky emailed me about his site, &lt;a href="http://www.rummy.com/"&gt;rummy.com&lt;/a&gt;, where you can find rules and background information for other rummy variants from around the world. You can also play them online.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-8389990052101224415?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/8389990052101224415/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=8389990052101224415' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/8389990052101224415'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/8389990052101224415'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2008/05/thai-rummy_16.html' title='Thai Rummy'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-4096447556770535266</id><published>2008-03-26T21:29:00.001-07:00</published><updated>2011-04-06T01:49:02.426-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><category scheme='http://www.blogger.com/atom/ns#' term='thinkpad'/><title type='text'>Thinkpad X61 Wireless and Ubuntu</title><content type='html'>At last I have resolved a long-standing gripe with Ubuntu on my ThinkPad X61.&lt;br /&gt;&lt;br /&gt;Months ago, soon after installing Gutsy Gibbon, I found that my wireless card initially worked well. But a few minutes later, my connection was dead, and I could only restore it by unloading the &lt;tt&gt;iwl4965&lt;/tt&gt; driver, reloading it, and reconnecting to my access point. Unfortunately, after an indeterminite amount of time the problem would reoccur.&lt;br /&gt;&lt;br /&gt;I experimented a little and discovered that by instructing my wireless router to accept anything only 802.11g, the problem vanished. When away from home and using other wireless networks, occasionally my connection would spuriously disconnect. Fortunately, the wireless networks I usually frequent somehow never cause this bug to manifest.&lt;br /&gt;&lt;br /&gt;Yesterday my wireless connection was unbearably flaky, and several times, even reloading the driver proved fruitless. I finally decided to look for a proper solution.&lt;br /&gt;&lt;br /&gt;Typing &lt;tt&gt;dmesg&lt;/tt&gt; revealed that every time the connection dropped, a message similar to the following would be logged:&lt;br /&gt;&lt;pre&gt;[  154.652000] wlan0: No ProbeResp from current AP 04:11:46:fe:24:b0 - assume out of range&lt;/pre&gt;&lt;a href="http://www.google.com/search?q=iwl4965+no+proberesp&amp;amp;btnG=Search"&gt;Googling for &lt;span style="font-family: monospace;"&gt;"&lt;/span&gt;&lt;tt&gt;iwl4965 no proberesp"&lt;/tt&gt;&lt;/a&gt; shows that many others have had the same trouble. Some pages were unanswered bug reports, and others contained discussion where the consensus was to reload the kernel module as a workaround. Happily, before long I stumbled across &lt;a href="http://tuxonline.nl/dcms/pages/FAQ+ThinkPad+T61.html"&gt;a FAQ for the ThinkPad T61&lt;/a&gt; that describes a permanent fix, which I'll repeat:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Download &lt;a href="http://www.intellinuxwireless.org/"&gt;the latest iwlwifi microcode&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Replace &lt;tt&gt;/lib/firmware/2.6.22-14-generic/iwlwifi-4965-1.ucode&lt;/tt&gt;.&lt;/li&gt;&lt;li&gt;Reload &lt;tt&gt;iwl4965&lt;/tt&gt; module&lt;/li&gt;&lt;/ol&gt;The new microcode banished my wireless woes, and the dreaded "No ProbeResp" no longer appears in the logs.&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;/span&gt;&lt;br /&gt;Now I'm left with one remaining significant complaint: the graphics driver. My i965 mostly behaves but there are minor annoyances involving compiz, and major ones involving Wine. I'll leave this for another time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-4096447556770535266?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/4096447556770535266/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=4096447556770535266' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4096447556770535266'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4096447556770535266'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2008/03/thinkpad-x61-wireless-and-ubuntu_26.html' title='Thinkpad X61 Wireless and Ubuntu'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-7825923307852281626</id><published>2008-03-20T01:59:00.001-07:00</published><updated>2011-04-06T01:49:02.429-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Cross-compiling GMP</title><content type='html'>I build Windows binaries from Ubuntu, using the &lt;a href="http://www.mingw.org/"&gt;MinGW&lt;/a&gt; port (search for packages starting with &lt;tt&gt;mingw&lt;/tt&gt;).&lt;br /&gt;&lt;br /&gt;For &lt;a href="http://crypto.stanford.edu/pbc/"&gt;the PBC library&lt;/a&gt; I need a Windows version of &lt;a href="http://gmplib.org/"&gt;the GMP library&lt;/a&gt;, which I build using:&lt;pre&gt;$ ./configure --prefix=~/cross/gmp --host=i586-mingw32msvc --build=local&lt;br /&gt;$ make install&lt;/pre&gt;I don't fully understand how it works, but when it's over, I have libraries I can link against to produce Windows executables that can do multi-precision arithmetic.&lt;br /&gt;&lt;br /&gt;I sometimes test the binaries with &lt;a href="http://www.winehq.org/"&gt;wine&lt;/a&gt;. Thus Windows can be completely avoided in the development cycle, from coding to building to testing. However, for an important project, I'd test on at least one real Windows box.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-7825923307852281626?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/7825923307852281626/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=7825923307852281626' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7825923307852281626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7825923307852281626'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2008/03/cross-compiling-gmp_20.html' title='Cross-compiling GMP'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-1878281332421996501</id><published>2008-03-17T23:44:00.001-07:00</published><updated>2011-04-06T01:49:02.433-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><category scheme='http://www.blogger.com/atom/ns#' term='thinkpad'/><title type='text'>xrandr</title><content type='html'>In the old days, to get the external VGA output working on my laptop in X, or even switch resolutions, I'd have to edit an overly complex configuration file and restart X. Nowadays, I use the X Resize and Rotate Extension via &lt;a href="http://www.thinkwiki.org/wiki/Xorg_RandR_1.2"&gt;xrandr&lt;/a&gt;, which is considerably more convenient. I like to control displays through the command-line interface. I find typing the full command cumbersome, so I use this simple script instead:&lt;br /&gt;&lt;pre&gt;#!/bin/bash&lt;br /&gt;case "$1" in&lt;br /&gt;  left)&lt;br /&gt;    xrandr --output VGA --auto --left-of LVDS&lt;br /&gt;    ;;&lt;br /&gt;  right)&lt;br /&gt;    xrandr --output VGA --auto --right-of LVDS&lt;br /&gt;    ;;&lt;br /&gt;  up)&lt;br /&gt;    xrandr --output VGA --auto --above LVDS&lt;br /&gt;    ;;&lt;br /&gt;  down)&lt;br /&gt;    xrandr --output VGA --auto --below LVDS&lt;br /&gt;    ;;&lt;br /&gt;  big)&lt;br /&gt;    xrandr --output VGA --mode 1600x1200 --above LVDS&lt;br /&gt;    ;;&lt;br /&gt;  off)&lt;br /&gt;    xrandr --output VGA --off&lt;br /&gt;    ;;&lt;br /&gt;  *)&lt;br /&gt;    xrandr --output VGA --auto --same-as LVDS&lt;br /&gt;    ;;&lt;br /&gt;esac&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-1878281332421996501?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/1878281332421996501/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=1878281332421996501' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1878281332421996501'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1878281332421996501'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2008/03/xrandr_17.html' title='xrandr'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-75975207982419127</id><published>2008-03-15T09:58:00.001-07:00</published><updated>2011-04-06T01:49:02.436-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><category scheme='http://www.blogger.com/atom/ns#' term='thinkpad'/><title type='text'>Thinkpad X61 Volume Buttons</title><content type='html'>I tried in vain to get the brightness adjustment keys to work, but at least the volume up and down buttons were easy to handle. They trigger bona fide X key events, so I added a couple of lines to &lt;tt&gt;&lt;a href="http://freshmeat.net/projects/xbindkeys/"&gt;.xbindkeys&lt;/a&gt;&lt;/tt&gt;:&lt;pre&gt;"amixer -c 0 set PCM 1dB+"&lt;br /&gt;  XF86AudioRaiseVolume&lt;br /&gt;&lt;br /&gt;"amixer -c 0 set PCM 1dB-"&lt;br /&gt;  XF86AudioLowerVolume&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-75975207982419127?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/75975207982419127/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=75975207982419127' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/75975207982419127'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/75975207982419127'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2008/03/thinkpad-x61-volume-buttons_15.html' title='Thinkpad X61 Volume Buttons'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-2224127770422007681</id><published>2008-03-04T00:01:00.001-08:00</published><updated>2011-04-06T01:49:02.439-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>GP2X Revisited</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_8FH64FXxxnM/R80CXRDPkuI/AAAAAAAAQ3k/29X-_YNIJgc/s1600-h/small-img_9334.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_8FH64FXxxnM/R80CXRDPkuI/AAAAAAAAQ3k/29X-_YNIJgc/s320/small-img_9334.jpg" alt="" id="BLOGGER_PHOTO_ID_5173794145655821026" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Soon after my last post, I tried out some simple ideas for smoothing out the spurious stylus events on my GP2X. The results were promising, but unfortunately before I could experiment anymore my device began malfunctioning. The left and right edges of the screen did not respond properly, and the problem quickly worsened. Soon I could only draw on about a third of the display, in a central vertical band.&lt;br /&gt;&lt;br /&gt;Luckily it was still under warranty. I sent it back to &lt;a href="http://www.gp2xstore.com/"&gt;www.GP2Xstore.com&lt;/a&gt;, who promptly passed it on to GamePark Holdings for repair. A few weeks later a replacement arrived. I still can't draw on the extreme left, right and bottom edges, but it works well enough for my primitive drawing program. The improved behaviour due to jitter correction is self-evident.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://cs.stanford.edu/%7Eblynn/20080304/touch2x-0.0.1.tar.bz2"&gt;touch2x-0.0.1.tar.bz2&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-2224127770422007681?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/2224127770422007681/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=2224127770422007681' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2224127770422007681'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2224127770422007681'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2008/03/gp2x-revisited_04.html' title='GP2X Revisited'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_8FH64FXxxnM/R80CXRDPkuI/AAAAAAAAQ3k/29X-_YNIJgc/s72-c/small-img_9334.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-5091623756406717353</id><published>2007-11-22T14:33:00.001-08:00</published><updated>2011-04-06T01:49:02.442-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='games'/><category scheme='http://www.blogger.com/atom/ns#' term='music'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Music Players and Game Consoles</title><content type='html'>Long ago, over a long period of time, I wore out my old MP3 player, a Cowon iAudio X5. First its battery life shortened dramatically. Then after severe mistreatment during a long flight, the headphone jack loosened and became very fussy. Only by pushing the plug in a certain way was I able to achieve stereo sound.&lt;br /&gt;&lt;br /&gt;Its condition worsened over time, and the final nail in the coffin was hammered in when, in desperation, I opened it up hoping I could readjust the socket somehow. I wound up breaking a wire.&lt;br /&gt;&lt;br /&gt;I could solder it back, but what's the use of bringing it back from the dead if it has a run-down battery and a messed-up audio jack? Besides, I now had a great excuse to shop.&lt;br /&gt;&lt;br /&gt;I considered getting another product in the same family, such as the A2, but in the end I took a different direction. Two different directions in fact: I wound up buying two MP3 players to replace my old one.&lt;br /&gt;&lt;br /&gt;First, some lessons learned:&lt;br /&gt;&lt;ul&gt;&lt;li&gt; I often just pushed play, and didn't bother fiddling around with the controls. Instead I'd cram it into my pocket, and perform other tasks.&lt;/li&gt;&lt;li&gt;The built-in battery was annoying, especially when traveling. I'd have to find adapters and/or transformers, or have a computer around. Not to mention carry a strange, easy-to-lose connector and a power cord.&lt;/li&gt;&lt;li&gt; Its utility greatly decreased with its battery life.&lt;/li&gt;&lt;li&gt; For some situations it was too big and unwieldy, while for others, such as when reading text files and playing movies, it was too small and unwieldy.&lt;/li&gt;&lt;/ul&gt;To address some of these concerns, I bought one of those small cheap MP3 player that is little more than a USB SD card reader. It's powered by a single AAA battery, and sports a tiny display and a few buttons. It's perfect for those times when I'm focused on something and want some background music. Instead of playlists, I treat SD cards like CDs, and swap in different cards when I want to change genres.&lt;br /&gt;&lt;br /&gt;It's convenient to travel with, due to its low cost and small footprint. Furthermore, AAA batteries are readily available anywhere. As a bonus, it can function as a low-capacity USB thumb drive.&lt;br /&gt;&lt;br /&gt;However, there are times when I want to read text files or watch movies on the go, and times when I absolutely must listen to that song right now. I also missed my iAudio's ability to play different music formats, and its ability to run the open source firmware Rockbox.&lt;br /&gt;&lt;br /&gt;So on an impulse, I recently picked up a GP2X F-200. It does music and video of various formats, but what really piqued my interest was that it plays games and runs Linux. It has two 200MHZ ARM9 processors, which amuses me because only three desktops ago, my system was a Pentium 200. And in line with my new philosophy, it has an SD card slot and is runs on 2 AA batteries.&lt;br /&gt;&lt;br /&gt;I still miss the 20 gigabyte hard drive, FM radio and recording capabilities of my X5, but the GP2X offers more possibilities.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Hello, GP2X F-200&lt;/span&gt;&lt;br /&gt;Mainstream gamers no doubt prefer the well-known brands for a hand-held gaming platform, as they have a wide selection of new titles and probably have better build quality. But my priorities are different: I mostly wanted a replacement music and video player, and the GP2X appeals to me thanks to its geek cred.&lt;br /&gt;&lt;br /&gt;There is a highly active GP2X development scene. It mostly revolves around simple homebrew games, ports of classics, and emulators, but there are a few commerical games, and some of the freeware offerings are amazingly good.&lt;br /&gt;&lt;br /&gt;One such gem is &lt;a href="http://en.wikipedia.org/wiki/Cave_Story"&gt;Cave Story&lt;/a&gt;, which I found on the bundled CD. I'd never heard of this game before, though it's been available for other platforms for quite some time. As a programmer and amateur game developer, I'm green with envy that one person alone was able to create this marvel.&lt;br /&gt;&lt;br /&gt;Soon after my unit arrived, I looked into how the development process works. I followed the &lt;a href="http://wiki.gp2x.org/wiki/Installing_the_Open2x_toolchain"&gt;Open2x installation instructions&lt;/a&gt; at &lt;a href="http://wiki.gp2x.org/wiki/Main_Page"&gt;the GP2X wiki&lt;/a&gt; (except I placed them in a different directory) but for touchscreen support, afterwards I installed the newest version of the SDL library found on &lt;a href="http://paeryn.myby.co.uk/"&gt;Paeryn's page&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I then set about writing the obligatory "Hello World" program. It's not good enough to print to standard output, because nothing shows up. Instead, I wrote a full-blown SDL program that uses the SDL_ttf library to render a string. Additionally, I had to write a shell script to return to the main menu after the program exits (and also sync the SD card).&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://cs.stanford.edu/~blynn/20071122/hello2x.tar.bz2"&gt;hello2x.tar.bz2&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;Next I augmented the code to experiment with the touch screen, and the result is a simple drawing program. I'm disheartened by many spurious events, though I have a few ideas on how to clean them up. This must be done if I'm to get the most out of this feature.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://cs.stanford.edu/~blynn/20071122/touch2x.tar.bz2"&gt;touch2x.tar.bz2&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;Green circles represent when the stylus was placed on the screen, and red circles represents when it was lifted, corresponding to SDL's MOUSEBUTTONDOWN and MOUSEBUTTONUP events. As can be seen, there are times when it thinks you've lifted the stylus when you haven't, and a fair amount of jitter, usually in the horizontal axis.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_8FH64FXxxnM/R0YJvutZfyI/AAAAAAAAMZM/yzCIoqmS2Ac/s1600-h/touch2x-screenshot.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_8FH64FXxxnM/R0YJvutZfyI/AAAAAAAAMZM/yzCIoqmS2Ac/s320/touch2x-screenshot.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5135803140659576610" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-5091623756406717353?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/5091623756406717353/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=5091623756406717353' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5091623756406717353'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5091623756406717353'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2007/11/music-players-and-game-consoles_22.html' title='Music Players and Game Consoles'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_8FH64FXxxnM/R0YJvutZfyI/AAAAAAAAMZM/yzCIoqmS2Ac/s72-c/touch2x-screenshot.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-9085111702902511041</id><published>2007-11-08T15:06:00.001-08:00</published><updated>2011-04-06T01:49:02.445-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><category scheme='http://www.blogger.com/atom/ns#' term='thinkpad'/><title type='text'>Linux on a Thinkpad X61</title><content type='html'>My X31 &lt;a href="http://benlynn.blogspot.com/2007/07/elective-thinkpad-x31-surgery.html"&gt;which I repaired a while ago&lt;/a&gt; died again. This time I think the system board is gone, as there are no signs of life. Nothing I do, even plugging in a power cord (and I tried several), will make any of the LEDs light up. It is functionally equivalent to a brick.&lt;br /&gt;&lt;br /&gt;I decided to purchase a Thinkpad X61 instead of a replacement system board. I chose to stick with the Thinkpad X series for several reasons. For starters, Thinkpads endure. Well, apart from my X31, though it still outlived other brands I've tried. I even have an old Thinkpad 240 that's still running strong.&lt;br /&gt;&lt;br /&gt;Another reason is that I want a good balance of small footprint and performance. I went through a phase long ago where I thought the biggest baddest laptop that money could buy was the right choice, but after trying it once, I discovered clunkiness destroys half the appeal of a laptop, and furthermore, a cheap desktop with accessories will give a much better computing experience.&lt;br /&gt;&lt;br /&gt;But most importantly, Thinkpads have a good track record with Linux support. Nonetheless, I did a few quick Google searches to confirm that this was the case for the X61. Happily there are &lt;a href="http://www.google.com/search?q=thinkpad+x61+linux"&gt;many results&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;My X61 arrived last month, and with Vista installed by default. Vista is astoundingly bad. Not only is it slow, but every few actions, everything grinds to a halt until I handle some dialog box about giving permission to some process or other.&lt;br /&gt;&lt;br /&gt;I suppose some effort was put into making the interface look slicker, but there is also plenty of extraneous rubbish that serve only as visual distractions and annoyances. Perhaps Microsoft is not entirely to blame, as some of the pop-ups are plastered with the Lenovo logo.&lt;br /&gt;&lt;br /&gt;In any case, my new system was unusable. I had to escape from Vista. It had been a long time since I had last installed Linux, as once setup correctly, there's rarely a reason to reinstall. I spent some time learning about the latest installation methods.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Network Installation&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;There's no need to prepare a floppy, CD or any other kind of removable media. Visit &lt;a href="http://www.goodbye-microsoft.com/"&gt;http://www.goodbye-microsoft.com/&lt;/a&gt; from any version of Windows, including Vista, and you can have Debian up and running within minutes.&lt;br /&gt;&lt;br /&gt;Note &lt;a href="http://lubi.sourceforge.net/unetbootin.html"&gt;UNetbootin&lt;/a&gt; is a similar program for Ubuntu, but Vista support is still in the works.&lt;br /&gt;&lt;br /&gt;There was one small hiccup. The Debian-installer loader ran fine, but a reboot brought be back to Vista (taking forever to load of course). After further research I discovered that I had to mess with the BCDEDIT program to set a nonzero delay so I could actually select Debian from the Vista boot menu.&lt;br /&gt;&lt;br /&gt;Then deja vu. I was reminded of the first time I tried installed a Linux desktop: back then, it must have taken me a day to get sound working, and another for the network.&lt;br /&gt;&lt;br /&gt;The wireless didn't work. Neither did the sound. The "vesa" graphics driver performed badly: switching back to text mode resulted in a blank screen. This system is too new for Debian.&lt;br /&gt;&lt;br /&gt;I had read success stories using the development version of Ubuntu ("Gutsy Gibbon"), and a few unsuccessful attempts at getting the wireless working I figured I'd switch.&lt;br /&gt;&lt;br /&gt;I tried to do so with UNetbootin (run from Debian) but I must have screwed something up because I was left with an unbootable system: the Grub bootloader had taken over the master boot record, and was somehow configured badly. To salvage the situation, I used &lt;a href="http://www.kegel.com/linux/pxe.html"&gt;a PXE network boot&lt;/a&gt;, and from a menu option I installed Gutsy.&lt;br /&gt;&lt;br /&gt;The wireless network card (Intel 4965) worked out of the box, as did the "intel" video driver for X windows. Back when I first tried this, and before Gutsy was officially released, sound didn't work and I had to download and compile the development version of ALSA's "hda_intel" driver to rectify this. But the latest version works out of the box.&lt;br /&gt;&lt;br /&gt;Various buttons such as the brightness and volume controls have no effect, but if I were so inclined, I could fix this by scripting commands with the aid of the ibm-acpi package.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Mission Accomplished&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;My laptop is now usable. Not only that, but it is orders of magnitude faster and smoother than any other system I use. I have more to say, but rather than delay this post yet again, I'll save the material for future articles.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-9085111702902511041?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/9085111702902511041/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=9085111702902511041' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/9085111702902511041'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/9085111702902511041'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2007/11/linux-on-thinkpad-x61_08.html' title='Linux on a Thinkpad X61'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-3170251380962051793</id><published>2007-09-20T20:26:00.001-07:00</published><updated>2011-04-06T01:49:02.447-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>AsciiDoc</title><content type='html'>It turns out I've been reinventing a wheel. &lt;a href="http://benlynn.blogspot.com/2007/09/wiki-formats.html"&gt;A few posts ago&lt;/a&gt; I spoke of creating a script that converts a text file to HTML, where the text file itself is highly readable. With clever ASCII usage, headings should look like headings, emphasized words should look like emphasized words, and so on, and there should be no ugly tags peppered throughout.&lt;br /&gt;&lt;br /&gt;I came across &lt;a href="http://www.methods.co.nz/asciidoc/"&gt;AsciiDoc&lt;/a&gt;, which does exactly this, and is far better, older, and more capable than my simple script. Interestingly, the format I came up with does not differ that much.&lt;br /&gt;&lt;br /&gt;I've converted a few pages on &lt;a href="http://www-cs-students.stanford.edu/~blynn/"&gt;my homepage&lt;/a&gt; to AsciiDoc. This format makes the most sense to me. HTML files with their space-hogging outlandish tags are unpleasant to read. Text files convey the same information, are easy on the eyes, and can be converted to HTML in no time.&lt;br /&gt;&lt;br /&gt;Also, why should I spend so much time making sure I'm using the right tag, and that my tags are nested properly and so on? Computers are supposed to do the mundane stuff for us. Generally speaking, computers should automate as much as possible so long as it is efficient and the process does not hamper human comprehension.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-3170251380962051793?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/3170251380962051793/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=3170251380962051793' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3170251380962051793'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3170251380962051793'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2007/09/asciidoc_20.html' title='AsciiDoc'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-6097372391856164739</id><published>2007-09-12T15:18:00.001-07:00</published><updated>2011-04-06T01:49:02.450-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><title type='text'>Automating Automounting</title><content type='html'>Newer, friendlier Linux distributions such as &lt;a href="http://www.ubuntu.com/"&gt;Ubuntu&lt;/a&gt; must surely make it easy to use CDs, CompactFlash and SD card readers, USB drives, external hard disks and so on. But I'm old-fashioned, and like to do things the hard way, that is, edit &lt;tt&gt;/etc/fstab&lt;/tt&gt; and run mount and umount manually so various devices end up at places I like in my directory.&lt;br /&gt;&lt;br /&gt;The problem is hot-pluggable devices get assigned different names in the &lt;tt&gt;/dev&lt;/tt&gt; directory all the time. So I followed the first part of &lt;a href="http://floatingsun.net/articles/howtos/howto-usb-automount.html"&gt;this guide&lt;/a&gt; that assigns the same name (that you choose) to the same device every time. Once this is done, you can modify &lt;tt&gt;/etc/fstab&lt;/tt&gt; in the usual way.&lt;br /&gt;&lt;br /&gt;While there, you might want to use the "noatime" mount option, or at least "relatime". See &lt;a href="http://linux.slashdot.org/article.pl?sid=07/08/08/1810243"&gt;this Slashdot article&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-6097372391856164739?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/6097372391856164739/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=6097372391856164739' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/6097372391856164739'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/6097372391856164739'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2007/09/automating-automounting_12.html' title='Automating Automounting'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-6916667443783228762</id><published>2007-09-07T18:07:00.001-07:00</published><updated>2011-04-06T01:49:02.453-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>Git Magic</title><content type='html'>As I promised in &lt;a href="http://benlynn.blogspot.com/2007/08/git-revisited.html"&gt;a previous post&lt;/a&gt;, I'd write more on tricks one can perform with Git. I had so much to say that I made a site out of it: &lt;a href="http://www-cs-students.stanford.edu/~blynn/gitmagic/"&gt;Git Magic&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;It was also a great opportunity to practice my salesmanship, as can be seen from some of section titles: Ultimate Backups, Light-Speed Multitasking, Guerrilla Version Control, Uninterrupted Workflow. It was a probably a lot more fun to write than it is to read.&lt;br /&gt;&lt;br /&gt;I'm hoping it will be useful to those new to version control, not just those new to Git. I took pains to explain the effects of Git branching. I had trouble understanding this from the official Git documentation, though it's probably better by now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-6916667443783228762?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/6916667443783228762/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=6916667443783228762' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/6916667443783228762'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/6916667443783228762'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2007/09/git-magic_07.html' title='Git Magic'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-5174916760188265547</id><published>2007-09-05T16:58:00.001-07:00</published><updated>2011-04-06T01:49:02.455-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Wiki Formats</title><content type='html'>For years I've dealt with raw HTML for a lot of my web pages. After some experience with various wiki sites, I've finally realized that it's a lot easier to maintain a bunch of simple text files marked up in some wiki format, and use clever scripts to convert them to HTML for publishing.&lt;br /&gt;&lt;br /&gt;Source code should always be readable, and HTML does not quite make the cut. Reading documentation in raw HTML form is painful. Inspired by the &lt;a href="http://www.inform-fiction.org/I7/Inform%207.html"&gt;Inform 7 language&lt;/a&gt;, I want my text file marked up with wiki to as close to a real document as possible. In other words, I want the source to be a pretty text file that you can read even if you don't have the HTML version. A bit like &lt;a href="http://txt2html.sourceforge.net/"&gt;txt2html&lt;/a&gt;, but I want more control. Compare &lt;a href="http://www-cs-students.stanford.edu/%7Eblynn//"&gt;my homepage&lt;/a&gt; with &lt;a href="http://www-cs-students.stanford.edu/%7Eblynn/index.txt"&gt;it's wiki source&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;And if you do need complex HTML, it's easy enough to provide a mechanism to temporarily disregard wiki tags.&lt;br /&gt;&lt;br /&gt;I wrote a script that takes converts a text file, marked up in a certain way, and converts it to HTML with these goals in mind, and it's kept in a Git repository at&lt;br /&gt;&lt;ul&gt;&lt;li&gt;http://cs.stanford.edu/~blynn/scrap/wiki.git&lt;/li&gt;&lt;/ul&gt;[20090608: updated link]&lt;br /&gt;I'll have to write instructions one day, but basically, headings are designated with equal signs  around the heading, two single quotes are italics, asterisks are used to mark text as bold, a minus sign and indentation for lists. Other indentations mean text that should be displayed unformatted and as fixed width.&lt;br /&gt;&lt;br /&gt;I'm gradually converting all my HTML to this wiki format, as it's easier to edit and maintain. An added bonus is that I can easily write scripts to output a different format, such as DocBook.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-5174916760188265547?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/5174916760188265547/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=5174916760188265547' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5174916760188265547'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5174916760188265547'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2007/09/wiki-formats_05.html' title='Wiki Formats'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-1030459177287313766</id><published>2007-08-29T16:55:00.001-07:00</published><updated>2011-04-06T01:49:02.458-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Git Revisited</title><content type='html'>To my consternation, the cogito package disappeared from Debian a few updates ago. I soon discovered the reason. Contrary to Linus Torvald's prognostications, raw &lt;a href="http://git.or.cz/"&gt;Git&lt;/a&gt; is now (verson 1.5) a user-friendly version control system and does not need wrappers.&lt;br /&gt;&lt;br /&gt;Still, I had to learn some new syntax. Luckily I was interested in learning about Git's more advanced features so I didn't mind. I was only previously using Git via Cogito in the same way one would use CVS or Subversion, except that it was faster, more robust and easier to use.&lt;br /&gt;&lt;br /&gt;Now I finally understand why Git can do so much more. Better late than never!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;101 Ways To Use Git&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Git is the duct tape of version control. When I first started using it, I only cared about a very specific problem and ignored Git's design and internals. I focused only on remembering the Cogito commands I needed to type to do what I want, oblivious to how it worked its magic. Relatively poor documentation is partly to blame, but in truth, it is extremely difficult to convey Git's extraordinary versatility.&lt;br /&gt;&lt;br /&gt;The book "Design Patterns" showcases the diverse problems that object-oriented design can elegantly solve. I wish there were an analgous Git book, instead of explaining Git fundamentals and leaving the reader to derive the implications, gave a set of recipes to accomplish a variety of tasks.&lt;br /&gt;&lt;br /&gt;Below are a few examples of Git recipes such a book might contain.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Instant Backup&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Just as in a computer game, when I'm about to attempt something drastic I like to save the current state, so I can go back and try again should things go awry.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;git-init&lt;br /&gt;git-add .&lt;br /&gt;git-commit -m "Initial commit"&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;to take a snapshot of all files in the current directory.&lt;br /&gt;Then if something goes wrong type&lt;br /&gt;&lt;code&gt;&lt;br /&gt;git-reset --hard&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;to go back to where you were. To save the state again, type &lt;tt&gt;git-commit -a&lt;/tt&gt;. You'll have to type in a description of the current state.&lt;br /&gt;&lt;br /&gt;One benefit to doing this instead of simply copying files is that with Git's hash chaining, you can tell if a backup gets corrupted.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Undo/Redo History&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;More generally, you can do the above, and every so often "save the game" by typing&lt;br /&gt;&lt;code&gt;&lt;br /&gt;git-commit -a -m "description of current state"&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Note if you want to keep track of newly added files or forget about deleted files you'll need to first run "git-add" or "git-delete" accordingly.&lt;br /&gt;&lt;br /&gt;Typing &lt;tt&gt;git-log&lt;/tt&gt; shows you a list of recent commits, and their SHA1 hashes. Then typing&lt;br /&gt;&lt;code&gt;&lt;br /&gt;git-commit -a&lt;br /&gt;git-revert SHA1_HASH&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;will restore the state to the commit with the given hash. You might like to use something like the following instead:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;git-revert "@{10 minutes ago}"&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;You can undo the undo: type &lt;tt&gt;git-log&lt;/tt&gt; and you'll see that the other commits you made are still there.&lt;br /&gt;&lt;br /&gt;To take the computer game analogy again, &lt;tt&gt;git-revert&lt;/tt&gt; is like loading a game and recording this fact as another saved game, and &lt;tt&gt;git-reset --hard&lt;/tt&gt; is like loading an old save and deleting all saved games newer than the one just loaded.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Sync Files Between Computers&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://benlynn.blogspot.com/2006/04/git-and-cogito.html"&gt;This is the reason I first used Git&lt;/a&gt;. I can make tarballs or use rsync to do backups. The problem was sometimes I'd edit on my laptop, other times on my desktop, and they may not have talked to each other in between.&lt;br /&gt;&lt;br /&gt;Initialize a Git repository and commit your files as above on one machine. Then on the other:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;git-clone git+ssh://other.computer/directory&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;to get a second copy. From now on,&lt;br /&gt;&lt;code&gt;&lt;br /&gt;git-commit -a&lt;br /&gt;git-pull git+ssh://other.computer/directory&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;will pull in the state of the files on the other computer into the one you're working on. If you've recently made conflicting edits in the same file, Git will let you know and you should resolve them with another edit and commit.&lt;br /&gt;&lt;br /&gt;If you have not committed any changes on the other computer, you may type&lt;br /&gt;&lt;code&gt;&lt;br /&gt;git-push git+ssh://other.computer/directory&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;to push the current state to the other computer. Next time you're there, run &lt;tt&gt;git-reset --hard&lt;/tt&gt; or &lt;tt&gt;git-checkout HEAD .&lt;/tt&gt; to update the files.&lt;br /&gt;&lt;br /&gt;That Git does not do the last step automatically is good in case you were in the middle of some uncommitted changes.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Tune in next time...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I've barely scratched the surface, and so far haven't shown anything you can't do with older version control systems. Next time I'll show off problems that only the new generation of version control can solve.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-1030459177287313766?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/1030459177287313766/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=1030459177287313766' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1030459177287313766'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1030459177287313766'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2007/08/git-revisited_29.html' title='Git Revisited'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-3314843103643473789</id><published>2007-07-23T15:48:00.001-07:00</published><updated>2011-04-06T01:49:02.460-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='thinkpad'/><title type='text'>Elective Thinkpad X31 Surgery</title><content type='html'>My trusty IBM Thinkpad X31 that Stanford bought me in 2003 has taken everything I've dished at it with only a couple of cracks in the case to show for it. At least, until recently. One awful afternoon, after hitting the power button I was briefly greeted with the message "Fan error" before it shut itself off. No amount of coaxing would bring it back.&lt;br /&gt;&lt;br /&gt;My desktop is truly ancient, dating back to the last millennium, so I had grown accustomed to doing most tasks on my laptop instead. The first task was to retrieve my data, which is easily achieved with a SATA/IDE to USB adapter. Only one screw needs to be removed to retrieve the hard drive.&lt;br /&gt;&lt;br /&gt;I was tempted to take the easiest but most expensive path: shell out for whatever passes for the best of the Thinkpad X series these days. But although working in industry means this remedy wouldn't sting as much as in my student days, it also means I don't spend as much time on my own machines. Now there's other things I'd prefer to get for that kind of money. Similarly, I didn't want to pay for it to be serviced.&lt;br /&gt;&lt;br /&gt;My only recourse was to attempt a repair myself. &lt;a href="http://www-307.ibm.com/pc/support/site.wss/document.do?lndocid=MIGR-44049"&gt;The Lenovo webpage about servicing the Thinkpad X31&lt;/a&gt; spooked me a little. What's going on in that diagram? Do I have to take it apart into a hundred pieces just to replace a fan? The table suggesting that fan replacements should not be undertaken by end users wasn't encouraging either. With a heavy heart, I ordered a 67P1443 fan anyway.&lt;br /&gt;&lt;br /&gt;When it arrived, enthusiasm and impatience triumphed and I took out every screw I could see, but the fan remained elusive. Eventually I regained some sense, and Googled for "thinkpad x31 disassembly". The first result contained a links leading to &lt;a href="http://www-307.ibm.com/pc/support/site.wss/document.do?lndocid=MIGR-44018"&gt;the official Thinkpad X31 hardware maintenance manual&lt;/a&gt;. I should have known. What isn't online these days?&lt;br /&gt;&lt;br /&gt;Fan replacements turn out to be almost trivial. After removing the battery and hard drive, I only had to remove the four clearly indicated keyboard screws, and then slide the keyboard out. Underneath, I discovered the reason for the fan error. Nestled in the fan blades was a tiny piece of foam, sticky on one side and rose-tinted on the other. A pink pad in my Thinkpad.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_8FH64FXxxnM/RqU2EJSv_II/AAAAAAAAMVo/ihUKddt33nU/s1600-h/small-img_5512.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_8FH64FXxxnM/RqU2EJSv_II/AAAAAAAAMVo/ihUKddt33nU/s320/small-img_5512.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5090534398654151810" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I suspect that the fan is still operational, but since I had a new one and had already removed the five screws securing it, I replaced it anyway. Also, it's easier to apply thermal paste to a new fan than clean and reapply to an old one.&lt;br /&gt;&lt;br /&gt;The tougher part was figuring out where the pink pad belonged. There was another one in there somewhere that also looked like it had been slightly dislodged. I settled on pressing both of them against some metal frame in the center, where I presume they work best as shock absorbers.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_8FH64FXxxnM/RqU1vZSv_HI/AAAAAAAAMVg/0imIAdUaVuA/s1600-h/small-img_5691.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_8FH64FXxxnM/RqU1vZSv_HI/AAAAAAAAMVg/0imIAdUaVuA/s320/small-img_5691.jpg" alt="" id="BLOGGER_PHOTO_ID_5090534042171866226" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Soon after I received my laptop I wrote &lt;a href="http://cs.stanford.edu/%7Eblynn/linuxthinkpadx31.html"&gt;a webpage about Linux on a Thinkpad X31&lt;/a&gt;. Only when I started blogging did I realize it would be much easier (for me at least!) to maintain with blog posts, and that's probably what I'll do in the future if I have more to add. Sure, it's nice to have all information in one place, but this can be approximated through appropriate use of tags. Furthermore, the dates on each post may also give the reader some idea of how relevant it is.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-3314843103643473789?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/3314843103643473789/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=3314843103643473789' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3314843103643473789'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3314843103643473789'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2007/07/elective-thinkpad-x31-surgery_23.html' title='Elective Thinkpad X31 Surgery'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_8FH64FXxxnM/RqU2EJSv_II/AAAAAAAAMVo/ihUKddt33nU/s72-c/small-img_5512.jpg' height='72' width='72'/><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-437798121932530922</id><published>2007-07-01T23:02:00.001-07:00</published><updated>2011-04-06T01:49:02.462-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>ALSA and MIDI</title><content type='html'>I switched to the ALSA driver from the OSS driver for my Creative Sound Blaster Live! card long ago, but I'm still not used to how MIDI is done. I'll record the relevant commands this time, so I won't have to look them up yet again.&lt;br /&gt;&lt;br /&gt;Lately, I've had to manually load the &lt;tt&gt;snd_seq&lt;/tt&gt; kernel module.&lt;br /&gt;&lt;br /&gt;Programs I wrote that look at &lt;tt&gt;/dev/midi00&lt;/tt&gt; should now look at &lt;tt&gt;/dev/snd/midiC0D0&lt;/tt&gt;. The behaviour of this device file differs too: I had to change my code so that it could handle a read() call that returned less than the requested number of bytes.&lt;br /&gt;&lt;br /&gt;To play MIDI files on my digital piano, I run &lt;tt&gt;pmidi -p 16:0 foo.mid&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;To send MIDI events to fluidsynth from my digital piano, I run &lt;tt&gt;fluidsynth 2&gt; /dev/null&lt;/tt&gt; (to get rid of spurious warnings), load a soundfont, then run &lt;tt&gt;aconnect 16:0 128:0&lt;/tt&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-437798121932530922?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/437798121932530922/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=437798121932530922' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/437798121932530922'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/437798121932530922'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2007/07/alsa-and-midi_01.html' title='ALSA and MIDI'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-2768468048920807899</id><published>2007-06-12T01:24:00.001-07:00</published><updated>2011-04-06T01:49:02.465-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><title type='text'>Nvidia GeForce 3 Cards on Debian</title><content type='html'>For a long time I used the Debian nvidia driver packages, but GeForce 3 Ti 200 cards are now considered legacy cards, and not supported by the 97xx and newer series. After a recent upgrade, I had to remove my Debian nvidia packages and&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Download &lt;a href="http://www.nvidia.com/object/linux_display_ia32_1.0-9639.html"&gt;the 96xx series Nvidia Linux drivers&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Run the install script as root.&lt;/li&gt;&lt;li&gt;Since I use the xorg X server, I had to move files around:&lt;br /&gt;&lt;code&gt;mv /usr/X11R6/lib/lib* /usr/lib/xorg/modules/&lt;br /&gt;mv /usr/X11R6/lib/modules/drivers/* /usr/lib/xorg/modules/drivers/&lt;br /&gt;mv /usr/X11R6/lib/modules/extensions/* /usr/lib/xorg/modules/extensions/&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-2768468048920807899?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/2768468048920807899/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=2768468048920807899' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2768468048920807899'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2768468048920807899'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2007/06/nvidia-geforce-3-cards-on-debian_12.html' title='Nvidia GeForce 3 Cards on Debian'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-5803147103560358101</id><published>2007-05-10T23:18:00.001-07:00</published><updated>2011-04-06T01:49:02.467-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>The BAD Library</title><content type='html'>Not long after my last post I wrote some code to get some rough timings, and also to experiment with variations of hash tables that my textbooks never taught me: with &lt;a href="http://en.wikipedia.org/wiki/Cuckoo_hashing"&gt;cuckoo hashing&lt;/a&gt; you can have expected constant-time insertion and &lt;i&gt;worst-case&lt;/i&gt; lookup time. Additionally, with cells, you get &lt;a href="http://www.cs.ucsc.edu/wdas06/Papers/Manasse.pdf"&gt;hash tables that can handle over 90% load&lt;/a&gt;. Also, hash functions recommended by classic textbooks are no good: &lt;a href="http://www.burtleburtle.net/bob/hash/"&gt;Bob Jenkins' hash functions&lt;/a&gt; are much better.&lt;br /&gt;&lt;br /&gt;Along the way I had to implement (or import from other projects of mine) other abstract data types. Soon I had crit-bit trees, hash tables, linked lists and dynamic arrays.&lt;br /&gt;&lt;br /&gt;I finally got around to packaging it up: &lt;a href="http://www-cs-students.stanford.edu/~blynn/20070510/bad-0.0.0.tar.bz2"&gt;bad-0.0.0.tar.bz2&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;BAD stands for Ben's Abstract Datatype, and also refers to the current state of the code and documentation. See the README for the preliminary results.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-5803147103560358101?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/5803147103560358101/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=5803147103560358101' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5803147103560358101'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5803147103560358101'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2007/05/bad-library_10.html' title='The BAD Library'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-7216779047602603851</id><published>2007-04-11T23:39:00.001-07:00</published><updated>2011-04-06T01:49:02.470-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Trees, Hash Tables and Tries</title><content type='html'>As a computer scientist, I'm supposed to possess thorough grounding in fundamental data structures and algorithms. I must be getting rusty, for lately I've been perturbed by basic facts taught to novices.&lt;br /&gt;&lt;br /&gt;After some thought I've resolved my difficulties, but now I feel many textbooks need to be rewritten. Either that, or I've made a monumental embarrassing mistake.&lt;br /&gt;&lt;br /&gt;Let's say you want an &lt;a href="http://en.wikipedia.org/wiki/Associative_array"&gt;associative array&lt;/a&gt; that maps strings to something.&lt;br /&gt;&lt;br /&gt;I've known for years that &lt;a href="http://www2.blogger.com/%20http://en.wikipedia.org/wiki/Hash_table"&gt;hash table&lt;/a&gt; lookups take O(1) time. And that &lt;a href="http://en.wikipedia.org/wiki/Binary_tree"&gt;binary search trees&lt;/a&gt; have O(log&lt;i&gt;n&lt;/i&gt;) lookup time. (In both cases I'm assuming certain conditions are satisfied, i.e. there aren't too many hash collisions and that the binary tree is fairly well-balanced.)&lt;br /&gt;&lt;br /&gt;More recently, I learned about &lt;a href="http://en.wikipedia.org/wiki/Radix_tree"&gt;crit-bit trees, aka radix trees, Patricia trees or Patricia tries&lt;/a&gt;. (See D. J. Bernstein's &lt;a href="http://cr.yp.to/critbit.html"&gt;crit-bit tree page&lt;/a&gt; for more references. Briefly, crit-bit trees are like binary trees with two major differences. Firstly, they are &lt;a ref="http://en.wikipedia.org/wiki/Trie"&gt;tries&lt;/a&gt;, which means keys are not stored in any non-leaf node and instead, the position of a node on a tree represents the prefix of the keys stored in its descendant leaves. Secondly, no node has only one child: a crit-bit tree is a trie where any node with a single child has been merged with its child.)&lt;br /&gt;&lt;br /&gt;What's the cost of a lookup in a radix tree? It sounds like it must be O(log&lt;i&gt;n&lt;/i&gt;) because you need a tree of that depth to store &lt;i&gt;n&lt;/i&gt; things.&lt;br /&gt;&lt;br /&gt;But you only need to touch each bit (or character, in a &lt;i&gt;k&lt;/i&gt;-ary tree where &lt;i&gt;k&lt;/i&gt; is the size of the alphabet) at most once during a lookup to decide which descendant of a node to traverse. Isn't this the same as determining the hash of a string? To compute it correctly we must involve every bit of the key. (If traversing the tree is cheap enough, then crit-bit trees are faster than hash tables. Furthermore, unlike a hashing operation, only certain bits, the aptly-named critical bits, need be examined in a crit-bit tree.)&lt;br /&gt;&lt;br /&gt;Thus the lookup cost of a radix tree must also be O(1). Therein lies a problem. Why is it that a binary tree of the same size has an O(log&lt;i&gt;n&lt;/i&gt;) lookup time? We travel along the same number of nodes!&lt;br /&gt;&lt;br /&gt;The answer is that usually when computer scientists analyze these data structures, as an approximation, we view computing the hash function as a constant-time operation, and similarly for a comparison in the binary tree. But to refer to &lt;i&gt;n&lt;/i&gt; different objects, the keys must have length at least O(log&lt;i&gt;n&lt;/i&gt;), which means it actually takes O(log&lt;i&gt;n&lt;/i&gt;) time to hash a key, or to perform a key comparison.&lt;br /&gt;&lt;br /&gt;So what we've been saying is that it's okay to ignore a factor of log&lt;i&gt;n&lt;/i&gt;. This is sloppy and inconsistent. Why can't we ignore the log&lt;i&gt;n&lt;/i&gt; steps required to walk down a binary tree as well? Why can we turn a blind eye to one log&lt;i&gt;n&lt;/i&gt; yet scrutinize the other?&lt;br /&gt;&lt;br /&gt;We should say that hash lookups and crit-bit tree lookups cost O(log&lt;i&gt;n&lt;/i&gt;) (or more accurately, O(&lt;i&gt;m&lt;/i&gt;) where &lt;i&gt;m&lt;/i&gt; is the size of the keys, which must be at least log&lt;i&gt;n&lt;/i&gt;), and that binary tree lookups cost O((log&lt;i&gt;n&lt;/i&gt;)&lt;sup&gt;2&lt;/sup&gt;).&lt;br /&gt;&lt;br /&gt;This inaccuracy caused me to initially overlook that crit-bit trees could match or even outperform hash tables. It's possible that others have suffered from the same affliction: despite being described (twice, independently) in 1968, the courses and textbooks from my time do not mention radix trees at all, and I suspect the teachers and authors responsible may also not have realized how fast these data structures can be.&lt;br /&gt;&lt;br /&gt;This is a shame because they have other advantages over hash tables too: crit-bit trees are deterministic with good worst-case times, grow and shrink as needed (they never require an expensive rehashing nor let vast tracts of an array lie fallow), and support successor and predecessor operations.&lt;br /&gt;&lt;br /&gt;The good news is that they seem to be gaining prominence. I feel they are mentioned more on the web (and I'm helping this as well!). I've heard evidence that some CS undergraduates these days are at least aware of their existence. The fact that even I know about them now means they can't be that obscure.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-7216779047602603851?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/7216779047602603851/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=7216779047602603851' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7216779047602603851'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7216779047602603851'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2007/04/trees-hash-tables-and-tries_11.html' title='Trees, Hash Tables and Tries'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-1747370181603535479</id><published>2007-03-29T01:41:00.001-07:00</published><updated>2011-04-06T01:49:02.474-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><category scheme='http://www.blogger.com/atom/ns#' term='puzzles'/><title type='text'>Prisoner Problems</title><content type='html'>Quite a few well-known logic puzzles involve a prison setting, giving them a darker tone that makes them memorable and more fun to think about.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://en.wikibooks.org/wiki/Puzzles/Escaping_from_Prison"&gt;This joke prisoner problem&lt;/a&gt; is amusing but impossible, as its rather surreal solution involves English homonyms.&lt;br /&gt;&lt;br /&gt;I also won't bother describing the famous &lt;a href="http://en.wikipedia.org/wiki/Prisoner%27s_dilemma"&gt;prisoner's dilemma&lt;/a&gt; from game theory.&lt;br /&gt;&lt;br /&gt;Then there's the paradox about the prisoner who's told he'll be executed some time next week and that it will be a surprise. So he reasons thus: "They can't kill me on Friday, because I would know by Thursday night and it would not be a surprise."&lt;br /&gt;&lt;br /&gt;"But this implies if they haven't killed me by Wednesday night, then I know I'm dead on Thursday because I can't be killed on Friday. Which means it wouldn't be a surprise. Hence I cannot be executed on Thursday."&lt;br /&gt;&lt;br /&gt;"Repeating this argument a few times shows that I cannot be executed on any day next week!" he concludes triumphantly.&lt;br /&gt;&lt;br /&gt;But on Tuesday morning he is executed, much to his surprise! What's going on?&lt;br /&gt;&lt;br /&gt;And there's the tale about the prisoner who is to choose his method of execution: if the last statement he utters is true then he is executed in some gruesome fashion, and on the other hand, if it is false then he is executed in some different but equally gruesome fashion. (The particular methods of execution change from telling to telling. I just can't think of any right now.) What should he say?&lt;br /&gt;&lt;br /&gt;A tougher puzzle involves one-hundred prisoners in a prison that contains one-hundred cells and a single room with a single light bulb that can be turned on or off. Every day a prisoner is chosen at random and placed in the room. At any time, any prisoner can ask for freedom. When he asks, if every prisoner has spent at least one day in the room with the light bulb then everybody is set free. (The warden has been keeping track.) Otherwise everybody is executed.&lt;br /&gt;&lt;br /&gt;The prisoners can talk amongst themselves before entering this bizarre jail, but once inside they are kept isolated, and the light bulb becomes their only means of communication. How can they free themselves?&lt;br /&gt;&lt;br /&gt;Lastly, there is a similar problem that is the main reason for this post. A friend told me this puzzle a few months ago and I don't want to forget it.&lt;br /&gt;&lt;br /&gt;Again, there are one-hundred prisoners and a special room, but this time the room contains one-hundred identical boxes, each containing exactly one slip of paper bearing a prisoner's name. Every prisoner's name is present, but no one knows which box contains which name.&lt;br /&gt;&lt;br /&gt;The prisoners are taken to the room one at a time. Once inside they are allowed to open and examine the contents of up to fifty boxes. They must then leave the room as they found it, so the boxes they chose are closed when they are done.&lt;br /&gt;&lt;br /&gt;After all prisoners have visited the room, if each prisoner has seen his name on a slip of paper then they are all free to go. Otherwise they are all executed.&lt;br /&gt;&lt;br /&gt;As before, they can all examine the room and boxes (but not open them) and devise a strategy together beforehand, but no communication is permitted once the process begins.&lt;br /&gt;&lt;br /&gt;Clearly if a prisoner picks fifty boxes at random he has only a 50% chance of finding his own name, and if every prisoner does this then the chance that all of them find their names is 1/2^100. That is, they will almost certainly be executed.&lt;br /&gt;&lt;br /&gt;How can they obtain at least a 30% chance of surviving?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-1747370181603535479?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/1747370181603535479/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=1747370181603535479' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1747370181603535479'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/1747370181603535479'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2007/03/prisoner-problems_29.html' title='Prisoner Problems'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-4244679141922593568</id><published>2007-03-07T01:23:00.001-08:00</published><updated>2011-04-06T01:49:02.480-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Shiny Colourful Buttons</title><content type='html'>Here's a trick I've seen mentioned in other geeky blogs that deserves to be more well-known and widespread: &lt;i&gt;only have page icons (&lt;a href="http://en.wikipedia.org/wiki/Favicon"&gt;favicons&lt;/a&gt;) on the Firefox bookmarks toolbar&lt;/i&gt;. Simply delete the names in bookmark properties. Or try the &lt;a href="https://addons.mozilla.org/firefox/4072/"&gt;Smart Bookmarks Bar&lt;/a&gt; extension. Note separators can be inserted to help organize the icons.&lt;br /&gt;&lt;br /&gt;Not only is it stylish, but it frees up precious toolbar real estate for more bookmarks. Also I find myself drawn to the pretty little pictures. I visit those enticing links more often nowadays.&lt;br /&gt;&lt;br /&gt;One might worry about forgetting which icon does what, but presumably the links on one's toolbar are frequently used, making this unlikely. Anyway, to find out where they go, just click on them! (i.e. the standard action following the thought: "I wonder what this button does...")&lt;br /&gt;&lt;br /&gt;The sole drawback is that some webpages don't have icons, or have one that looks identical to another, or looks horrible. To solve this, use the &lt;a href="https://addons.mozilla.org/firefox/3176/"&gt;Favicon Picker 2 extension&lt;/a&gt; to assign a good icon to that bookmark.&lt;br /&gt;&lt;br /&gt;A related idea is to hide text on certain tabs, which can be achieved using the &lt;a href="https://addons.mozilla.org/firefox/3780/"&gt;FaviconizeTab extension&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;By the way, googling for "favicon picker" finds many of the aforementioned blog posts [&lt;a href="http://www.cre8d-design.com/blog/2006/05/17/managing-bookmarks-tip/"&gt;1, &lt;/a&gt;&lt;a href="http://lifehacker.com/software/firefox/download-of-the-day-part-ii-favicon-picker-firefox-extension-113421.php"&gt;2&lt;/a&gt;, &lt;a href="http://fairlyclever.com/2006/09/11/favicon-picker/"&gt;3&lt;/a&gt;, &lt;a href="http://www.lockergnome.com/nexus/web/2006/11/08/favicon-picker-2/"&gt;4&lt;/a&gt;, &lt;a href="http://www.downloadsquad.com/2006/05/29/favicon-picker-for-firefox/"&gt;5&lt;/a&gt;], some containing great screenshots. Not everyone is enthralled by the idea however, and &lt;a href="http://rbenson.wordpress.com/2005/10/22/firefox-hide-bookmark-toolbar-icons/"&gt;some even want the opposite&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-4244679141922593568?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/4244679141922593568/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=4244679141922593568' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4244679141922593568'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4244679141922593568'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2007/03/shiny-colourful-buttons_07.html' title='Shiny Colourful Buttons'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-4999789161847368461</id><published>2007-02-21T17:38:00.001-08:00</published><updated>2011-04-06T01:49:02.483-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>A New Beginning</title><content type='html'>This is the last post that will appear on my office server at Stanford. It will be disconnected soon, after many years of faithful service.&lt;br /&gt;&lt;br /&gt;This is also the first new post at this blog's new home at &lt;a href="http://www.blogger.com/"&gt;Blogger&lt;/a&gt;. It's good to have everything automated: timestamps, formatting, archives, permalinks, labels, etc. but part of me misses writing my own scripts to do all this, and the total control I had over everything.&lt;br /&gt;&lt;br /&gt;Converting my old blog entries was much easier than I had anticipated, though it was not entirely smooth. For example, I have to keep source code on a separate server because this site only lets you put up HTML and images. Also, I never kept the time the old entries were posted, only the date, so I had to fabricate them.&lt;br /&gt;&lt;br /&gt;In retrospect, I realize I've taken a rather roundabout route to blogging. I would never have gone out of my way to sign up on some website to start writing articles, out of the blue. Instead, a peripheral page on my website intended to help me keep track of changes gradually mutated into this.&lt;br /&gt;&lt;br /&gt;I plan to relocating other parts of my site. The &lt;a href="http://crypto.stanford.edu/pbc/"&gt;PBC Library&lt;/a&gt; already lives on a more permanent server. I recently moved &lt;a href="http://code.google.com/p/xmmspipe/"&gt;xmmspipe&lt;/a&gt; to &lt;a href="http://code.google.com/"&gt;Google Code&lt;/a&gt;. This service seems quite nice but I dislike the fact that they only support Subversion: I am now an ardent &lt;a href="http://benlynn.blogspot.com/2006/04/git-and-cogito.html"&gt;git&lt;/a&gt; fan.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-4999789161847368461?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/4999789161847368461/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=4999789161847368461' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4999789161847368461'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4999789161847368461'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2007/02/new-beginning_21.html' title='A New Beginning'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-2115261291740323336</id><published>2007-02-14T01:00:00.001-08:00</published><updated>2011-04-06T01:49:02.486-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='games'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Chase the Pig</title><content type='html'>&lt;p&gt; I was surprised to find that Wikipedia currently lacks an entry for the card game 拱豬 (Gong Zhu). This four-player trick-taking Hearts-like game is often played by my relatives on my mother's side, and I assume it's relatively well-known amongst the Chinese. &lt;/p&gt; &lt;p&gt; &lt;a href="http://www.pagat.com/reverse/gongzhu.html"&gt;John McLeod maintains a page describing various rulesets for Gong Zhu&lt;/a&gt; (in particular the variants involving exposing cards sound highly intriguing to me), but none of them exactly match the one I was taught. For posterity I'll record my family's rules here. &lt;/p&gt; &lt;p&gt; For the first deal, the player with the seven of spades leads the first trick. The lead can be any card, not necessarily the seven of spades. In subsequent deals, the player that took the Queen of Spades in the previous deal leads the first trick. This player is sometimes nicknamed "The Pig". As in Hearts, there are no trumps, and the winner of the trick leads the next trick. &lt;/p&gt; &lt;p&gt; Why the seven of spades and not the two? Because another card game popular amongst my aunts and uncles involved building sequences starting with seven, and the first card played in that game had to be a seven. They adopted this rule for consistency. &lt;/p&gt; &lt;p&gt; My family always played a predetermined number of hands (e.g. 4) and the winner was the player(s) with the highest total score, but the more standard convention seems to be that one keeps playing until someone has a total score of -1000 or lower, and then the player(s) with the highest score is the winner. (I'm guessing they did this because when playing for stakes, payoffs occur more frequently with this scheme.) &lt;/p&gt; &lt;p&gt; The values of the cards are as follows: &lt;/p&gt; &lt;ul&gt;&lt;li&gt; Jack of Diamonds, or goat (羊): +100     &lt;/li&gt;&lt;li&gt; Queen of Spades, or pig (豬): -200     &lt;/li&gt;&lt;li&gt; Ten of Clubs: doubles your score, unless you have won no other cards in which case it is worth +50     &lt;/li&gt;&lt;li&gt; Hearts: two to ten are worth minus their pip value, except for four which is worth -10. The Jack is -20, Queen -30, King -40 and Ace -50. The other cards are worth nothing.     &lt;/li&gt;&lt;/ul&gt; &lt;p&gt; I was taught a couple of mnemonics for the Four of Hearts being -10. Firstly, 4 is an unlucky number amongst the Chinese (I was told this is because the word for 4 sounds similar to the verb "to die" in Mandarin, and also in other variants of Chinese), so that's why its penalty value is worse than it should be. &lt;/p&gt; &lt;p&gt; Secondly, the word for 4 and the word for 10 in Mandarin sound similar. If you know a Mandarin speaker, get them to say "44 is 44" to hear for yourself! &lt;/p&gt; &lt;p&gt; If a player wins all the hearts, then the values of the hearts are reversed in sign, that is, they are positive instead of negative, and it can be checked that they are worth 200 points in total. Furthermore, if that same player also wins the pig, the pig is now worth +200 points. &lt;/p&gt; &lt;p&gt; If you win every card with a value, then you receive 100 points for the goat, 200 for the sheep, 200 for all the hearts, and finally your score gets doubled by the Ten of Clubs, giving a total of 1000 points. This is is the best possible score. The worst possible score is -796, when all but the Jack of Diamonds and Two of Hearts is taken. &lt;/p&gt; &lt;p&gt; Naturally, the name of the game refers to the practice of leading low spades in an attempt to flush out the pig: eventually the holder of the Queen of Spades may be forced to play it and take the trick. &lt;/p&gt; &lt;h2&gt;Comparison with Hearts&lt;/h2&gt; &lt;p&gt; I prefer this game to Hearts. There is something special about every suit in this game, whereas in Hearts, the clubs and diamonds tricks feel like filler. Winning the Jack of Diamonds is always good, and winning the Ten of Clubs sometimes helps, so even if you're not going for all the hearts there is something to do. &lt;/p&gt; &lt;p&gt; I never liked the Hearts rule preventing players from "breaking into" hearts, and I'm glad that Gong Zhu is free from this restriction. &lt;/p&gt; &lt;p&gt; Scoring is more complicated as each heart has a different penalty value. However I found after several hands I enjoyed the less trivial mental arithmetic, even missing it when playing Hearts. &lt;/p&gt; &lt;p&gt; Generally, I feel the game experience is richer because there are more important cards than Hearts, yet the additional complexity is not overly random nor overwhelming. In contrast, while playing Hearts if I'm not trying to shoot the moon, my play feels almost forced, as my only goal is to avoid the Queen of Spades and as many hearts a possible. Sometimes I can chose who to penalize more by playing in a certain way, but this aspect of the game is limited and not very rewarding. &lt;/p&gt; &lt;p&gt; Even when shooting the moon, the strategy is often clear, a drawback that I feel is exacerbated by the prohibition on breaking into penalty cards. &lt;/p&gt; &lt;p&gt; I'm ambivalent about the Hearts convention of passing three cards before the game starts. Sometimes it's extremely helpful since some people I've played with are rather predictable, allowing me to mold hands which I can easily shoot the moon with. On the other hand, this does reduce the challenge, and in general getting extra information about opponent's hands detracts from the game experience. &lt;/p&gt; &lt;p&gt; The internal conflicts are more intense and exciting. I can feel my greed fight my fear. Do I save high cards to win the goat? What if this causes me to wind up with the pig and/or a bunch of high hearts as well? Do I go for the double? It's 50 points on its own, but how sure am I that I won't pick up any hearts later on? &lt;/p&gt; &lt;h2&gt;Other Fun Card Games&lt;/h2&gt; &lt;p&gt; I recall being enthralled when introduced to playing cards as a child. Nothing but fifty-two bits of pasteboard, yet the possibilities are legion. Games of pure skill. Games of pure chance. Games that fell in between, and it seemed that a game existed for any given skill/luck ratio. And they can be played almost anywhere, with any number of people, even alone. Such power, and I could hold it in one hand. Merely shuffling and performing sleights at once soothed and inspired me. To this day I often carry a pack of cards on my person. &lt;/p&gt; &lt;p&gt; One of my well-loved books from my childhood was &lt;a href="http://www.biblio.com/isbn/0883653893.html"&gt;"The Book of Games" by Richard Sharp and John Piggott (ISBN 0883653893)&lt;/a&gt;. I read it cover to cover countless times, though not necessarily in order, marvelling at the illustrations and fascinated by the history. I tried out many of the games within, goading whoever was around me to learn their obscure rules so I could play. &lt;/p&gt; &lt;p&gt; (I took offence to one sentence however: in the entry on Mah Jong it describes the Chinese as "devious". I don't think they're inherently better at games than other races!) &lt;/p&gt; &lt;p&gt; If only the internet and Wikipedia existed back then! I was frustrated by rules they omitted, not to mention the games they left out. Sometimes the descriptions were too concise due to space limitations. Today the rules of just about any game with some following are at one's fingertips, and one can sometimes even find free programs for an instant opponent, instructor and umpire. Unfortunately, this came after my heaviest gaming days (at least, games that don't involve computers!), so the games below are mostly from the above book. &lt;/p&gt; &lt;p&gt; Though I'll mention card games of many species, some bias will be noticeable since I'm fond of lightweight teamless trick-taking games. Player ability varies wildly in some circles, making partner selection problematic in games such as bridge and canasta. &lt;/p&gt; &lt;p&gt; If there are four players, I almost always enjoy playing Hearts and Chase the Pig. &lt;a href="http://en.wikipedia.org/wiki/Big_2"&gt;Big Two&lt;/a&gt; is another favourite. &lt;/p&gt; &lt;p&gt; When there are three players, &lt;a href="http://www.pagat.com/reverse/knaves.html"&gt;Knaves&lt;/a&gt; is a good choice if I'm in the mood for something like Hearts. It is also a no-trump trick-taking game with penalty cards, but also gives one an incentive to win as many tricks as possible. &lt;a href="http://en.wikipedia.org/wiki/3-5-8"&gt;Sergeant Major&lt;/a&gt; is an entertaining diversion for three, especially for those newer to trick-taking games. My friends and family found &lt;a href="http://en.wikipedia.org/wiki/Ninety-nine_%28card_game%29"&gt;David Parlett's Ninety-nine&lt;/a&gt; refreshingly unique and challenging. (We played the original rules, as described in The Book of Games.) &lt;/p&gt; &lt;p&gt; I never found two-player card games appealing, though I feel like I could if I dedicated more time to them. In particular, I feel like I should like &lt;a href="http://en.wikipedia.org/wiki/Cribbage"&gt;Cribbage&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Bezique"&gt;Bezique&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Piquet"&gt;Piquet&lt;/a&gt;. I've tried some novel two-player games described in "The Pan Book of Card Games" by Hubert Philips, but never grew attached to them. &lt;/p&gt; &lt;p&gt; For five players or above, I'd play poker, or in a less serious crowd, &lt;a href="http://en.wikipedia.org/wiki/Bartog"&gt;Bartog&lt;/a&gt;. &lt;/p&gt; &lt;p&gt; Also, Napoleon is a five-player trick-taking game whose gimmick is the existence of a secret partnership: the identity of highest bidder, Napoleon, is known, but his secretary is determined by the holder of a particular card that is not revealed until played during a trick. Thus it is a two-versus-three game where initially no one knows the composition of the teams. &lt;/p&gt; &lt;p&gt; I had forgotten the details of this game. But thanks to Google, I rediscovered this old friend, and learned a lot more. Apparently, my parents left out at least half the rules when they taught it to me. Also, it seems &lt;a href="http://www.pagat.com/picture/napoleon.html"&gt; Napoleon is a popular Japanese card game&lt;/a&gt;. &lt;/p&gt; &lt;p&gt; As for patience/solitaire for one, I'd recommend downloading a program like &lt;a href="http://www.pysol.org/"&gt;PySol&lt;/a&gt; and exploring. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-2115261291740323336?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/2115261291740323336/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=2115261291740323336' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2115261291740323336'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2115261291740323336'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2007/02/chase-pig_14.html' title='Chase the Pig'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-8846736308595107908</id><published>2006-11-25T01:00:00.001-08:00</published><updated>2011-04-06T01:49:02.489-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Inkscape and Valgrind</title><content type='html'>&lt;p&gt;I spend a lot more time at &lt;a href="http://crypto.stanford.edu/pbc/"&gt;the PBC (Pairing-Based Cryptography) library site&lt;/a&gt; than I would like since I maintain those pages. I bet most webmasters have similar complaints. &lt;/p&gt; &lt;p&gt; I began to tire of seeing page after page of text, so I broke the monotony by adding a site logo. It serves no other purpose, but who knows? Maybe one day I'll be hawking coffee mugs and T-shirts emblazoned with this graphic! &lt;/p&gt; &lt;p&gt; &lt;img src="http://crypto.stanford.edu/pbc/pbclogo.png" alt="PBC logo" height="64" width="64" /&gt; &lt;/p&gt; &lt;p&gt; I went through several ideas before selecting a shape, during which I gained respect for professional logo designers. Some famous logos seem so simple that I feel I could have designed them. But on further examination, they insiduously invade and reside in one's memory, are pleasing to the eye (or at least are not eyesores), and in fact satisfy &lt;a href="http://en.wikipedia.org/wiki/Logo#Logo_design"&gt;many constraints&lt;/a&gt;. &lt;/p&gt; &lt;p&gt; After a few attempts I discovered that it is extremely diffcult to find symbols with the necessary properties, and in my case, to even judge how effective a given image is. Thus I settled on a design as soon as I sketched one that I could tolerate. &lt;/p&gt; &lt;p&gt; I recall the path I took. At one point I was experimenting with a stylized "P(B,C)". I removed the letters to get "(,)" and played around with the parentheses and comma until I arrived at the current logo. &lt;/p&gt; &lt;p&gt; As for the colour scheme, to pay homage to my current university, I let &lt;a href="http://www.stanford.edu/group/identity/ug_color.html"&gt;Stanford's design guidelines on colour&lt;/a&gt; dictate my choices. Thus the logo is predominantly cardinal, with sandstone and black playing secondary roles. On most desktops, many screen elements are white and hence all four Stanford "Identity Colors" appear. &lt;/p&gt; &lt;p&gt; The result reminds me of a symbol featured in the movie "&lt;a href="http://www.imdb.com/title/tt0317705/"&gt;The Incredibles&lt;/a&gt;". I hope this causes people to think the PBC library is incredible, at least subconsciously! &lt;/p&gt; &lt;p&gt; I used &lt;a href="http://www.inkscape.org/index.php"&gt;Inkscape&lt;/a&gt; to realize my idea. I had never tried Inkscape before. I was surprised at how easy it was for a newbie to use and how quickly I became comfortable and efficient with its interface. &lt;/p&gt; &lt;p&gt; (Strictly speaking, this is false. Many moons ago I fired up Inkscape's ancestor &lt;a href="http://www.sodipodi.com/index.php3"&gt;Sodipodi&lt;/a&gt;, but I was unable to do much without reading documentation. Either Sodipodi has since made great advances, or the fork was worth the trouble.) &lt;/p&gt; &lt;p&gt; While I'm on my soapbox lauding software, let me also praise &lt;a href="http://valgrind.org/"&gt;Valgrind&lt;/a&gt;. I first heard of this program years ago. If only I had tried it years ago. &lt;/p&gt; &lt;p&gt; Finding memory leaks in my C projects had always been troublesome. Thanks to geek machismo [isn't this an oxymoron?], I chose the hard way, namely, to code wrappers around standard memory allocation functions that recorded statistics which I would then analyze to detect leaks. &lt;/p&gt; &lt;p&gt; Since my leak detection code would need to be bug-free itself for this to work, I would restrict myself to primitive designs to simplify implementation details, ultimately leaving much of the sleuthing to the developer. Furthermore, toggling leak detection was so annoying I rarely checked for leaks. &lt;/p&gt; &lt;p&gt; As I recently discovered, Valgrind magically gives detailed reports on where the leak is and what is being leaked [&lt;a href="http://en.wikipedia.org/wiki/Hewlett-Packard#HP_pretexting_scandal"&gt;insert HP joke here&lt;/a&gt;]. And to use Valgrind one only needs to invoke gcc with different options during compilation. &lt;/p&gt; &lt;p&gt; Valgrind has other features too, but I haven't tried them yet. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-8846736308595107908?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/8846736308595107908/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=8846736308595107908' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/8846736308595107908'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/8846736308595107908'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2006/11/inkscape-and-valgrind_25.html' title='Inkscape and Valgrind'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-4027530357815273582</id><published>2006-10-29T01:00:00.001-07:00</published><updated>2011-04-06T01:49:02.492-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>Pi Music</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_8FH64FXxxnM/SoO_kRE26kI/AAAAAAAAYnA/0PxGAG6Ur6M/s1600-h/friend.png"&gt;&lt;/a&gt;&lt;p&gt;I must have spent most of my life facing one keyboard or another. Over a year ago, while facing a musical kind, I was in a weird whimsical mood and I wrote a tune based on the digits of pi. &lt;/p&gt; &lt;p&gt; It did not surprise me when I discovered I was on a well-beaten path, as &lt;a href="http://www.google.com/search?q=pi+music"&gt;a search on Google&lt;/a&gt; quickly reveals. After all, many pi-inspired feats have been accomplished, such as &lt;a href="http://users.aol.com/s6sj7gt/mikerav.htm"&gt;setting Edgar Allen Poe's poem "The Raven" to the digits of pi&lt;/a&gt;. See also &lt;a href="http://www.irtc.org/stills/1997-06-30.html"&gt;the honourable mention in this ray-tracing competition&lt;/a&gt;. &lt;/p&gt; &lt;p&gt; The pi music I found was quite abstract, whereas I want mine to sound more standard. I chose to have the digits 1-7 represent the tones of the C minor scale, altered if necessary, while 8 and 9 wrap around, that is, I encoded 8 and 1 the same way, as well as 9 and 2. Accordingly, I would have treated a 0 as a 7 but the tune ends just before reaching the first 0. (Thus 31 decimal places are encoded, this number fortunately corresponding with the first two digits of pi.) &lt;/p&gt; &lt;p&gt; This is akin to &lt;a href="http://en.wikipedia.org/wiki/Numbered_musical_notation"&gt;jianpu&lt;/a&gt;, a numbered musical notation system which I see in many Chinese books, but have never encountered in the Western world though supposedly it has seem some use in Europe. My scheme differs in that with jianpu, one would normally notate a minor key starting from 6, not 1. &lt;/p&gt; &lt;p&gt; It's a shame jianpu is not more widespread. It is well-suited for quickly and concisely jotting down simple melodies. A blank piece of paper suffices: there is no need to rule a music staff first. Writing numbers and dashes can be done extremely fast and most people are accustomed to this, unlike drawing geometric figures. More sloppiness is tolerated, as it is easier to identify scrawled digits and dashes than it is to figure out which line or space a hastily-scribbled note lies on, or whether a certain dot belongs to a note or was just a slip of then pen. Jianpu is also easy to learn due to its logic and simplicity. (Of course, jianpu performs terribly for even a slightly complex piece.) &lt;/p&gt; &lt;p&gt; (To be precise, jianpu-like notation is in fact employed in Western music. For example, when one writes "C6" to denote a chord, the "6" refers to the sixth note of the major scale.) &lt;/p&gt; &lt;p&gt; I added extra notes in order to produce a repeated motif typical of tunes. Following the digits exactly produces a melody that sounds a little too random for my tastes. &lt;/p&gt; &lt;p&gt; I chose conventional chord progressions. The A section uses a chromatically descending bassline, and the bridge mostly follows the cycle of fifths. &lt;/p&gt; &lt;p&gt; The time signature is 3/4, as this resembles 3.14 superficially. Also, in early (Western) musical notation, this time signature was represented with a circle. &lt;/p&gt; &lt;p&gt; I had intended to post it only once I had made some recordings (ideally after working on &lt;a href="http://cs.stanford.edu/~blynn/bliss/"&gt;Bliss&lt;/a&gt;) but I have enough on my plate as it is. Maybe next &lt;a href="http://en.wikipedia.org/wiki/Pi_day"&gt;Pi Day&lt;/a&gt;. &lt;/p&gt; &lt;p&gt; Anyway, it feels like an auspicious time to release it, as I happened to have recently released version 0.3.14 of &lt;a href="http://crypto.stanford.edu/ibe/"&gt;the PBC library&lt;/a&gt;. And not too long ago Akira Haraguchi broke the world record after &lt;a href="http://science.slashdot.org/article.pl?sid=06/10/05/2219247"&gt;memorizing 100,000 digits of pi&lt;/a&gt;. &lt;/p&gt; &lt;p&gt; The lead sheets were created using Lilypond. The &lt;a href="http://lilypond.org/"&gt;Lilypond website&lt;/a&gt; convincingly argues that their software automatically produces better sheet music than their major competitors, including commercial ones. It's also easy to integrate its output with HTML:&lt;/p&gt;&lt;p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_8FH64FXxxnM/SoO_TnDQ6dI/AAAAAAAAYm4/Ldnr90X5U7g/s1600-h/pi.png"&gt;&lt;img src="http://4.bp.blogspot.com/_8FH64FXxxnM/SoO_TnDQ6dI/AAAAAAAAYm4/Ldnr90X5U7g/s400/pi.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5369345524376857042" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 400px; height: 208px; " /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Incidently, around the same time I also penned a tune that loosely encodes a friend's name, but using a different method:&lt;/p&gt;&lt;p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_8FH64FXxxnM/SoO_kRE26kI/AAAAAAAAYnA/0PxGAG6Ur6M/s1600-h/friend.png"&gt;&lt;img src="http://1.bp.blogspot.com/_8FH64FXxxnM/SoO_kRE26kI/AAAAAAAAYnA/0PxGAG6Ur6M/s400/friend.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5369345810535737922" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 400px; height: 153px; " /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_8FH64FXxxnM/SoO_TnDQ6dI/AAAAAAAAYm4/Ldnr90X5U7g/s1600-h/pi.png"&gt;&lt;/a&gt;&lt;/p&gt; &lt;ul&gt;&lt;li&gt; Pi: &lt;a href="http://cs.stanford.edu/~blynn/20061029/pi.pdf"&gt;pi.pdf&lt;/a&gt; [&lt;a href="http://cs.stanford.edu/~blynn/20061029/pi.ps"&gt;pi.ps&lt;/a&gt;] [&lt;a href="http://cs.stanford.edu/~blynn/20061029/pi.ly"&gt;pi.ly&lt;/a&gt;]     &lt;/li&gt;&lt;li&gt; For a friend: &lt;a href="http://cs.stanford.edu/~blynn/20061029/friend.pdf"&gt;friend.pdf&lt;/a&gt; [&lt;a href="http://cs.stanford.edu/~blynn/20061029/friend.ps"&gt;friend.ps&lt;/a&gt;] [&lt;a href="http://cs.stanford.edu/~blynn/20061029/friend.ly"&gt;friend.ly&lt;/a&gt;]&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-4027530357815273582?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/4027530357815273582/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=4027530357815273582' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4027530357815273582'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4027530357815273582'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2006/10/pi-music_29.html' title='Pi Music'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_8FH64FXxxnM/SoO_TnDQ6dI/AAAAAAAAYm4/Ldnr90X5U7g/s72-c/pi.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-240691392210700196</id><published>2006-09-27T01:00:00.001-07:00</published><updated>2011-04-06T01:49:02.496-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>CMake and Autotools</title><content type='html'>&lt;p&gt; I'm very grateful that somebody (Joe Cooley) contributed Autoconf and Automake files for the &lt;a href="http://rooster.stanford.edu/%7Eben/pbc/"&gt;PBC library&lt;/a&gt; that I maintain. The GNU Autotools are almost essential because many people expect its presence and are used to compiling packages on Unix by typing &lt;tt&gt;./configure&lt;/tt&gt; followed by &lt;tt&gt;make&lt;/tt&gt;. For the same reason, the Autotools give the project a more mature appearance. And they really do allow painless compilation on many systems. &lt;/p&gt; &lt;p&gt; But there is a dark side to using the &lt;a href="http://en.wikipedia.org/wiki/GNU_build_system"&gt;GNU build system&lt;/a&gt;. The PBC source packages increased by several hundred kilobytes. I notice this extra weight because I often use wireless networks. Additionally, build times are at least twice as slow. On one run, it took 37 seconds to compile PBC, compared to 13 seconds with the original handwritten &lt;tt&gt;Makefile&lt;/tt&gt;. And that's not counting the 13 seconds for &lt;tt&gt;./configure&lt;/tt&gt; and the 4 seconds for &lt;tt&gt;./setup&lt;/tt&gt;. &lt;/p&gt; &lt;p&gt; Furthermore, one has to be fluent in m4 and various configuration file formats, and understand how the toolchain works to create and maintain the build system. I was never motivated enough to learn all this, and I haven't changed. Any modifications I make to the Autoconf and Automake files are based on what I see there already, or a brief Google search. &lt;/p&gt; &lt;p&gt; It seems some people were sufficiently dissatisfied with the Autotools that they offered &lt;a href="http://www.software-carpentry.com/"&gt;a substantial sum to anyone who wrote a better build tool&lt;/a&gt;. The winning project was &lt;a href="http://www.scons.org/"&gt;SCons&lt;/a&gt;, which I experimented with last time I needed a build tool with more features than make. &lt;/p&gt; &lt;p&gt; But now a different tool has caught my attention, ever since I read  &lt;a href="http://lwn.net/Articles/188693/"&gt;this article&lt;/a&gt;. The &lt;a href="http://www.kde.org/"&gt;KDE project&lt;/a&gt; ran into trouble with the Autotools, and began searching for alternatives. Like me, they found SCons promising, but after further investigation decided to switch to &lt;a href="http://www.cmake.org/HTML/Index.html"&gt;CMake&lt;/a&gt;. &lt;/p&gt; &lt;p&gt; Porting KDE to different platforms is highly nontrivial, so I reasoned that if CMake worked for such a substantial project, odds are good that it would work for my tiny library. I'm following by example I guess, and it certainly isn't the first time. I use &lt;a href="http://git.or.cz/"&gt;git&lt;/a&gt; because it works for managing the &lt;a href="http://www.kernel.org/"&gt;Linux kernel source&lt;/a&gt;, and I use C because it works for countless important parts of the system (including of course the kernel), and for many well-respected programmers across several generations. &lt;/p&gt; &lt;p&gt; While researching CMake I found more arguments for switching: &lt;a href="http://freshmeat.net/articles/view/889/"&gt;autoconf/automake is insane&lt;/a&gt;, &lt;a href="http://lwn.net/Articles/198171/"&gt;glowing reviews of CMake (in the comments section)&lt;/a&gt;, &lt;a href="http://blog.qgis.org/?q=node/16"&gt;another endorsement&lt;/a&gt; and &lt;a href="http://tools.devchannel.org/article.pl?sid=03/12/15/2139255&amp;mode=thread&amp;amp;tid=46"&gt;an interview with a CMake author&lt;/a&gt;. &lt;/p&gt; &lt;p&gt; I had a &lt;tt&gt;CMakeLists.txt&lt;/tt&gt; file up and running fairly quickly, as the syntax is quite simple. Running times are much better: CMake took a couple of seconds to generate a &lt;tt&gt;Makefile&lt;/tt&gt;, which in turn only took a second longer than my handwritten one to compile PBC. I was also pleased by the clean and colourful output. &lt;/p&gt; &lt;p&gt; Unfortunately, CMake is not installed on many systems. And it must be relatively untested. How does it compare to the Autotools on less mainstream platforms? And what about the more obscure tasks that the Autotools excel at? For example, is it difficult to get CMake to cross-compile a project? &lt;/p&gt; &lt;p&gt; For now, I'll have to maintain two sets of build configuration files in parallel, but I hope one day CMake will be equally well-known and widespread so I can jettison the autoconf/automake baggage. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-240691392210700196?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/240691392210700196/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=240691392210700196' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/240691392210700196'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/240691392210700196'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2006/09/cmake-and-autotools_27.html' title='CMake and Autotools'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-2188832513637212162</id><published>2006-09-21T01:00:00.001-07:00</published><updated>2011-04-06T01:49:02.499-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>A Tale of Two Hacks</title><content type='html'>&lt;p&gt; Years ago I wrote &lt;a href="http://rooster.stanford.edu/%7Eben/rcenter.html"&gt;rcenter&lt;/a&gt; which has long been superseded by the &lt;a href="http://www.lirc.org/"&gt;LIRC project&lt;/a&gt;. I finally switched to LIRC myself. I wonder if anyone out there is still using rcenter. &lt;/p&gt; &lt;p&gt; It took me a while to figure out that LIRC does not work with OSS drivers unless &lt;a href="http://users.adelphia.net/%7Esbeahm/lirc/lirc_livedrive_howto.html"&gt; Stephen Beahm's midi poll patch&lt;/a&gt; has been applied. I decided to switch to the ALSA drivers to avoid this issue. But then I had to set some other module options: &lt;tt&gt;snd-emu10k1 enable_ir=1 extin="0x3fc3" extout="0x1fff"&lt;/tt&gt;. &lt;/p&gt; &lt;p&gt; I wrote &lt;a href="http://code.google.com/p/xmmspipe/"&gt;xmmspipe&lt;/a&gt; almost simultaneously, and in contrast, this project isn't obsolete yet. In fact, I recently discovered it has been an official &lt;a href="http://packages.gentoo.org/ebuilds/?xmms-pipe-0.5.3"&gt; Gentoo Linux package&lt;/a&gt; for some time when I received a bugfix for it. &lt;/p&gt; &lt;p&gt; It's times like these when I feel warm and fuzzy. I get to experience first-hand some of the touted benefits of free software. Having the source open means thankfully &lt;a href="http://bugs.gentoo.org/show_bug.cgi?id=144842"&gt; someone else can solve the problem&lt;/a&gt;. Even if I managed to fix the bug myself, I probably would've spent hours doing so. &lt;/p&gt; &lt;p&gt; On the other hand, I wouldn't mind if xmmspipe were obsolete and named pipes came with XMMS by default. I'm reminded of a quote from &lt;a href="http://en.wikipedia.org/wiki/Doug_McIlroy"&gt;Doug McIlroy&lt;/a&gt;: &lt;/p&gt;&lt;blockquote&gt; &lt;p&gt; This is the Unix philosophy. Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface. &lt;/p&gt; &lt;/blockquote&gt; and one from &lt;a href="http://en.wikipedia.org/wiki/Rob_Pike"&gt;Rob Pike&lt;/a&gt;: &lt;blockquote&gt; &lt;p&gt; There has been much talk about component architectures, but only one true success: Unix pipes. &lt;/p&gt; &lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-2188832513637212162?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/2188832513637212162/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=2188832513637212162' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2188832513637212162'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2188832513637212162'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2006/09/tale-of-two-hacks_21.html' title='A Tale of Two Hacks'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-8516289874127542277</id><published>2006-09-14T01:00:00.001-07:00</published><updated>2011-04-06T01:49:02.508-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tricks'/><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><title type='text'>Mental Feats</title><content type='html'>&lt;p&gt; Michael Curtis (who commented on &lt;a href="http://rooster.stanford.edu/%7Eben/blog/20060717/mnemonic-major-systems.html"&gt;a previous posting&lt;/a&gt;) has written &lt;a href="http://www.nakedscience.com/"&gt;several articles on mental feats involving memory and mathematics&lt;/a&gt;. &lt;/p&gt; &lt;p&gt; Coincidently, I too read about the &lt;a href="http://en.wikipedia.org/wiki/Trachtenberg_system"&gt;Trachtenberg system&lt;/a&gt; (a method for performing mental arithmetic) many years ago. &lt;/p&gt; &lt;p&gt; Reading his site reminded me of a time when I'd relieve boredom during occasions such as high school assemblies by squaring 2-digit numbers in my head using the techniques I had read about. &lt;/p&gt; &lt;p&gt; Today, I'd still use Trachtenberg's method for numbers ending in 5, based on the equation (10 a + 5)^2 = 100 a (a+1) + 25. However I have since found faster methods for other numbers, which I haven't seen described on the web. They only require additions and subtractions, but one has more to memorize. &lt;/p&gt; &lt;p&gt; Let n be the 2-digit number to be squared. Then if n lies in the range: &lt;/p&gt; &lt;ol&gt;&lt;li&gt;     0-25: Memorize these answers.     &lt;/li&gt;&lt;li&gt;     25-50: Work out how far n is from 50 and     how far n is from 25. Then the answer is 100(n - 25) + (50 - n)^2.     &lt;/li&gt;&lt;li&gt;     50-75: Compute 100((n-50) + 25) + (n-50)^2.     (This is also Trachtenberg's method for squaring fifty-somethings.)     &lt;/li&gt;&lt;li&gt;     75-100: Compute 100(100 - 2(100-n)) + (100-n)^2     &lt;/li&gt;&lt;/ol&gt; &lt;p&gt; While I'm at it, I'll record a method for finding square roots (of squares of 2-digit numbers): &lt;/p&gt; &lt;ol&gt;&lt;li&gt;Remove the last two digits of the square. Then the first digit of     the answer is the largest digit whose square is less than this number.     &lt;/li&gt;&lt;li&gt;The last digit of the square tells us what the last digit of the     answer could be. If it is 0 or 5, then so is the last digit of the answer     and we are done, otherwise:     &lt;/li&gt;&lt;li&gt;     Let the first digit of the answer is a. Compare the square with     the square of 10a+5. If larger, then the last digit of the answer is     between 6 and 9, and if smaller, it is between 1 and 4. Luckily, in base     10, the squares of 1 to 4 have distinct last digits. Also     the squares of a and (10-a) end in the same digit, so it is now easy     to determine the last digit of the answer.     &lt;/li&gt;&lt;/ol&gt; &lt;p&gt; The corresponding algorithm for cube roots is much simpler, because the cube of each digit has a distinct last digit (and similarly with other odd powers).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-8516289874127542277?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/8516289874127542277/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=8516289874127542277' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/8516289874127542277'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/8516289874127542277'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2006/09/mental-feats_14.html' title='Mental Feats'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-579951325563440007</id><published>2006-08-18T01:00:00.001-07:00</published><updated>2011-04-06T01:49:02.514-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Slideshows in Firefox</title><content type='html'>&lt;p&gt; Like many geeks, I frequently use text-based interfaces where normal people use GUIs, with a sense of smug self-satisfaction. Instead of a &lt;a href="http://en.wikipedia.org/wiki/WYSIAYG"&gt;WYSIAYG&lt;/a&gt; word processor I use typesetting software like &lt;a href="http://www.latex-project.org/"&gt;LaTeX&lt;/a&gt;. No fancy website creators for me, I use &lt;a href="http://www.vim.org/"&gt;gvim&lt;/a&gt; to edit HTML files. Spreadsheets? I keep data in flat text files and write scripts to process them. &lt;/p&gt; &lt;p&gt; How about presentations? I had used &lt;a href="http://member.wide.ad.jp/wg/mgp/"&gt;MagicPoint&lt;/a&gt; for a few, but I wasn't completely satisfied. For instance, equations were fiddly: I had to write a script that would run TeX to render the equations to encapsulated PostScript and embed the resulting image in the slideshow. I briefly thought about writing my own program. Very briefly. Then I thought about exploiting existing programs instead. &lt;/p&gt; &lt;p&gt; I had come across &lt;a href="http://sourcefrog.net/projects/pinpoint/"&gt;PinPoint&lt;/a&gt; which uses GIMP to produce great-looking slides from a few lines. GIMP was designed to manipulate and display text and images, and is scriptable. But for live presentations, and for certain features I wanted, other programs or scripts would be needed, requiring a fair amount of work. &lt;/p&gt; &lt;p&gt; An idea hit me. MagicPoint can convert slides to HTML. How difficult would it be to modify things slightly so that presentations can be done in a web browser? After all, web browsers also manipulate and display text and images from a simple language. Not only that, they were designed to show different pages in succession. They are also ubiquitous. &lt;/p&gt; &lt;p&gt; One would just need to display pages in fullscreen, and perhaps using Javascript, have certain keypresses cause certain actions such as changing slides and triggering animations and other effects. Soon after experimenting with this, I discovered I was definitely not the first to think about web-based presentations. &lt;/p&gt; &lt;p&gt; The &lt;a href="http://www.opera.com/"&gt;Opera browser&lt;/a&gt; has long had a slide show feature (the &lt;a href="http://en.wikipedia.org/wiki/Opera_Show_Format"&gt;Opera Show Format&lt;/a&gt;), but unfortunately it is not supported by other browsers. I want it to work on &lt;a href="http://www.mozilla.com/firefox/"&gt;Firefox&lt;/a&gt;. &lt;/p&gt; &lt;p&gt; Luckily, an alternative, the &lt;a href="http://en.wikipedia.org/wiki/S5_file_format"&gt;S5 project&lt;/a&gt;, has surfaced, which creates slideshows from a few lines of XHTML, and should work on any standards-compliant browser. &lt;/p&gt; &lt;p&gt; S5 was just what I was looking for. Webpages can contain images, text, visual effects, animations, and so on, and in theory S5 presentations should be able to as well. &lt;/p&gt; &lt;h2&gt;MathML in S5&lt;/h2&gt; &lt;p&gt; I want to display equations via MathML, but at present one cannot simply embed MathML (or SVG) and change the MIME type of S5 slides accordingly, though &lt;a href="http://meyerweb.com/eric/thoughts/2006/01/09/s5-12a1/"&gt;a fix exists and will be released&lt;/a&gt;. &lt;/p&gt; &lt;p&gt; As a workaround, I use &lt;a href="http://www1.chapman.edu/%7Ejipsen/asciimath.html"&gt; ASCIIMathML&lt;/a&gt;. Perhaps this is a good thing. I had intended to put LaTeX style equations in the middle of the HTML and use &lt;a href="http://pear.math.pitt.edu/mathzilla/itex2mml.html"&gt;itex2mml&lt;/a&gt; to convert it to MathML, but since ASCIIMathML converts to MathML on-the-fly using JavaScript, I can skip the compilation step. (Other tools to convert human-friendly text to MathML are &lt;a href="http://www.blahtex.org/"&gt;blahtex&lt;/a&gt;, &lt;a href="http://www.orcca.on.ca/MathML/texmml/textomml.html"&gt;TexToMathML&lt;/a&gt;, and &lt;a href="http://hutchinson.belmont.ma.us/tth/mml/"&gt;TtM&lt;/a&gt;.) &lt;/p&gt; &lt;p&gt; There's still the matter of getting the equations to display on Firefox. Until the &lt;a href="http://stixfonts.org/"&gt;STIX Fonts&lt;/a&gt; are ready, extra &lt;a href="http://www.mozilla.org/projects/mathml/fonts/"&gt;mathematical fonts have to be manually installed&lt;/a&gt;. &lt;/p&gt; &lt;p&gt; Also, for months now, &lt;a href="http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=361183"&gt; MathML does not display correctly on certain Linux systems, though a workaround exists&lt;/a&gt; [&lt;a href="http://www.ibiblio.org/litlfred/mathBlog/mathml/"&gt;also described here&lt;/a&gt;]. And for some reason, S5 is extremely slow on my Debian system, but runs fine on the Windows build of Firefox. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-579951325563440007?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/579951325563440007/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=579951325563440007' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/579951325563440007'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/579951325563440007'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2006/08/slideshows-in-firefox_18.html' title='Slideshows in Firefox'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-8576811380528901945</id><published>2006-07-17T01:00:00.001-07:00</published><updated>2011-04-06T01:49:02.519-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tricks'/><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Mnemonic Major Systems</title><content type='html'>&lt;p&gt; I discovered two things when I tried using the net to brush up on &lt;a href="http://en.wikipedia.org/wiki/Major_system"&gt;mnemonic major systems (aka phonetic number systems)&lt;/a&gt;. (If you have no idea what they are read the article before continuing!) Firstly, the only encoding from digits to sounds I've seen online so far is the one published by Harry Lorayne. Secondly, it turned out I didn't need any brushing up at all. The mapping I learnt many years ago was so easy to remember that, not only can I still recall it, but the memory is so strong I cannot use Lorayne's system without confusion. &lt;/p&gt; &lt;p&gt; I came across the major system I use in a book by Jean Hugard. I argue that it is more natural and easier to learn. There were only a few rules to remember: &lt;/p&gt; &lt;p&gt; 1,2,3 correspond to the consonants l,m,n respectively. This is easy to remember since the letters require 1,2, and 3 strokes to write. If you happen to know the British sign language alphabet, observe that you place 1, 2 or 3 fingers on the other hand's palm when signing l, n and m respectively. &lt;/p&gt; &lt;p&gt; Some mappings are based on the way you pronounce digits in English. 4 is r (think “fou&lt;b&gt;rrr&lt;/b&gt;”), 5 is f or v (think “&lt;b&gt;f&lt;/b&gt;i&lt;b&gt;v&lt;/b&gt;e”). 0 is s or z (think “&lt;b&gt;z&lt;/b&gt;ero”). &lt;/p&gt; &lt;p&gt; Then there are the digits that look like letters. 6 is b or p, 7 is t, th or d. 9 is k or g. With sufficiently bad handwriting (or fonts), 6 and b are indistinguishable, as are 9 and g (or q, which in English is always pronounced starting with a k or g sound, and always as a k sound in several European languages). 7 and T are also similar. &lt;/p&gt; &lt;p&gt; The only rule I never liked much was the one for 8 (which is not a problem since being the odd one out makes it easy to remember!): “Eight” sounds like “aitch”, which hopefully helps you remember that the sh, ch (and j) sounds correspond to 8. &lt;/p&gt; &lt;table&gt;     &lt;tbody&gt;&lt;tr&gt;  &lt;th&gt;Digit&lt;/th&gt;  &lt;th&gt;Consonant(s)&lt;/th&gt;  &lt;th&gt;Reason&lt;/th&gt;     &lt;/tr&gt;     &lt;tr&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;l&lt;/td&gt;  &lt;td&gt;strokes&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;  &lt;td&gt;2&lt;/td&gt;  &lt;td&gt;n&lt;/td&gt;  &lt;td&gt;strokes&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;  &lt;td&gt;3&lt;/td&gt;  &lt;td&gt;m&lt;/td&gt;  &lt;td&gt;strokes&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;  &lt;td&gt;4&lt;/td&gt;  &lt;td&gt;r&lt;/td&gt;  &lt;td&gt;sound&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;  &lt;td&gt;5&lt;/td&gt;  &lt;td&gt;f, v&lt;/td&gt;  &lt;td&gt;sound&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;  &lt;td&gt;6&lt;/td&gt;  &lt;td&gt;b, p&lt;/td&gt;  &lt;td&gt;shape&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;  &lt;td&gt;7&lt;/td&gt;  &lt;td&gt;d, t, th&lt;/td&gt;  &lt;td&gt;shape&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;  &lt;td&gt;8&lt;/td&gt;  &lt;td&gt;ch, j, sh&lt;/td&gt;  &lt;td&gt;special&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;  &lt;td&gt;9&lt;/td&gt;  &lt;td&gt;k, g, q&lt;/td&gt;  &lt;td&gt;shape&lt;/td&gt;     &lt;/tr&gt;     &lt;tr&gt;  &lt;td&gt;0&lt;/td&gt;  &lt;td&gt;s, z&lt;/td&gt;  &lt;td&gt;sound&lt;/td&gt;     &lt;/tr&gt; &lt;/tbody&gt;&lt;/table&gt; &lt;p&gt; I also mostly prefer the mapping from playing cards to words as presented by Hugard. Although I dislike the aces been treated specially and agree with Lorayne assigning the names of the suits to the jacks, I believe thinking KH as a groom and QH as a bride for example is easier to learn. &lt;/p&gt; &lt;p&gt; However, there is at least one practical benefit to Lorayne's system. By &lt;a href="http://en.wikipedia.org/wiki/Benford%27s_law"&gt;Benford's law&lt;/a&gt;, it is more likely that a number one wishes to memorize begins with a 1, and it is easier to think of a word starting with d, t or th than to think of one starting with l. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-8576811380528901945?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/8576811380528901945/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=8576811380528901945' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/8576811380528901945'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/8576811380528901945'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2006/07/mnemonic-major-systems_17.html' title='Mnemonic Major Systems'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-380812825650997684</id><published>2006-05-26T01:00:00.001-07:00</published><updated>2011-04-06T01:49:02.523-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Firefox and Manpages</title><content type='html'>&lt;p&gt; I prefer to read manpages in a web browser rather than in a terminal or a text editor. &lt;a href="http://www.konqueror.org/"&gt;Konqueror&lt;/a&gt; formats manpages very nicely [&lt;a href="http://www.raybenjamin.com/linux-help-how-to/ar01s04.html"&gt;screenshots here&lt;/a&gt;], but I use Firefox, and do not use KDE. &lt;/p&gt; &lt;p&gt; I wish there were a Firefox extension to handle manpages in a similar way to Konqueror. Instead, I use &lt;a href="http://www.oac.uci.edu/indiv/ehood/man2html.html"&gt;man2html&lt;/a&gt;. I setup a &lt;a href="http://www.mozilla.org/products/firefox/smart-keywords.html"&gt; Smart Keyword&lt;/a&gt; so that typing &lt;tt&gt;man firefox&lt;/tt&gt; for example retrieves the manpage for Firefox. &lt;/p&gt; &lt;p&gt; Unfortunately I feel its output is not as pleasing as Konqueror's. As a stopgap measure, I use the &lt;a href="http://greasemonkey.mozdev.org/"&gt;Greasemonkey Firefox extension&lt;/a&gt; to add some CSS to make the pages more to my liking. &lt;/p&gt; &lt;ul&gt;&lt;li&gt;     &lt;a href="http://cs.stanford.edu/~blynn/20060526/script.js"&gt;script.js&lt;/a&gt;: Greasemonkey script that activates every time man2html is invoked on localhost. Adds a CSS file to the page.     &lt;/li&gt;&lt;li&gt;     &lt;a href="http://cs.stanford.edu/~blynn/20060526/man2html.css"&gt;man2html.css&lt;/a&gt;: CSS file that changes the look of the manpages.     &lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-380812825650997684?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/380812825650997684/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=380812825650997684' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/380812825650997684'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/380812825650997684'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2006/05/firefox-and-manpages_26.html' title='Firefox and Manpages'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-3856773048195544471</id><published>2006-04-11T01:00:00.001-07:00</published><updated>2011-04-06T01:49:02.527-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Web Syndication and Atom Feeds</title><content type='html'>&lt;p&gt; Due to popular demand ;-) I added a &lt;a href="http://en.wikipedia.org/wiki/Web_feed"&gt;web feed&lt;/a&gt; for my blog. [Much to my surprise I discovered today that there is at least one occasional reader of this blog besides me. Thanks for your kind words.] &lt;/p&gt; &lt;p&gt; I experimented with web feeds when I first heard about the &lt;a href="http://www.mozilla.com/firefox/livebookmarks.html"&gt;Live Bookmarks&lt;/a&gt; feature of &lt;a href="http://www.mozilla.com/"&gt;Firefox&lt;/a&gt;. It wasn't so long ago, and the process was straightforward: I created a few &lt;a href="http://en.wikipedia.org/wiki/RSS_%28file_format%29"&gt;RSS&lt;/a&gt; 0.91 files, and filled them with bookmarks, each entry using a title, description and link tag. &lt;/p&gt; &lt;p&gt; Now I find that RSS has forked into two different formats, confusingly named “RSS 1.0” and “RSS 2.0”. (In fact, there are more versions of RSS.) A third contender, &lt;a href="http://en.wikipedia.org/wiki/Atom_%28standard%29"&gt;Atom&lt;/a&gt; has surfaced. &lt;/p&gt; &lt;p&gt; It seems Atom 1.0 is the most fashionable choice, so I wrote a script to generate an Atom feed for this blog. &lt;/p&gt; &lt;p&gt; Atom is more complicated than RSS 0.91. Dates must be included and have to be in &lt;a href="http://www.rfc-archive.org/getrfc.php?rfc=3339"&gt;RFC 3339&lt;/a&gt; format. Unfortunately the date command in Debian stable is too old to support the &lt;tt&gt;--rfc-3339&lt;/tt&gt; switch, so instead I used &lt;/p&gt; &lt;blockquote&gt; &lt;p&gt; &lt;tt&gt;date +%Y-%m-%dT%H:%M:%S%z $* | sed 's/..$/:&amp;amp;/'&lt;/tt&gt; &lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt; Some sort of ID is also required, and I'm not sure exactly what the purpose of this is. The specifications refer to some &lt;a href="http://en.wikipedia.org/wiki/Internationalized_Resource_Identifier"&gt;Internationalized Resource Identifier&lt;/a&gt; as defined by &lt;a href="http://www.rfc-archive.org/getrfc.php?rfc=3987"&gt;RFC 3987&lt;/a&gt; but that's one standard too many for now. At the moment I use the relevant URL as the ID. &lt;/p&gt; &lt;p&gt; Ideally the mime-type of an Atom file should be served as “application/atom+xml”, which in Apache can be accomplished by adding &lt;/p&gt; &lt;blockquote&gt; &lt;p&gt; &lt;tt&gt;AddType application/atom+xml .atom&lt;/tt&gt; &lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt; in a configuration file. &lt;/p&gt; &lt;p&gt; In a HTML file, one should add something like: &lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;tt&gt; &amp;lt;link rel="alternate" type="application/atom+xml" title="Title" href="http://some.url/something.atom"&amp;gt; &lt;/tt&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt; and perhaps also lines like: &lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;tt&gt; &amp;lt;a href="http://some.url/something.atom"&amp;gt;&amp;lt;img src="http://some.url/feed-icon-16x16.png" alt="Site Feed" height="16" width="16" /&amp;gt;&amp;lt;/a&amp;gt; &amp;lt;a href="http://some.url/something.atom"&amp;gt;Site Feed&amp;lt;/a&amp;gt; &lt;/tt&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt; where the feed icons can be downloaded from &lt;a href="http://www.feedicons.com/"&gt;feedicons.com&lt;/a&gt;. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-3856773048195544471?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/3856773048195544471/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=3856773048195544471' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3856773048195544471'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3856773048195544471'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2006/04/web-syndication-and-atom-feeds_11.html' title='Web Syndication and Atom Feeds'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-5829808474448482907</id><published>2006-04-10T01:00:00.001-07:00</published><updated>2011-04-06T01:49:02.531-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Git and Cogito</title><content type='html'>&lt;p&gt; Somehow, I have managed to avoid &lt;a href="http://en.wikipedia.org/wiki/Revision_control"&gt;revision control&lt;/a&gt; systems until recently. I set it up for a typical reason: I wanted to be able to work on my thesis from different computers without worrying too much about keeping different copies in sync. Also, it's nice to be able to keep track of my progress. I don't expect to ever revert to an older version, but seeing a detailed log of changes makes me feel I've accomplished something. &lt;/p&gt; &lt;p&gt; Perhaps it's a good thing I put off learning about version control for so long. Back when I first thought about using it, &lt;a href="http://en.wikipedia.org/wiki/Concurrent_Versions_System"&gt;CVS&lt;/a&gt; was the most popular open-source choice, &lt;a href="http://en.wikipedia.org/wiki/Subversion_%28software%29"&gt;Subversion&lt;/a&gt; was immature, and Linus Torvalds didn't use any system for the Linux kernel. &lt;/p&gt; &lt;p&gt; Now a new generation of source control tools has materialized, and are being improved rapidly. Development of the Linux kernel has benefitted ever since Linus started used revision control systems years ago. &lt;/p&gt; &lt;p&gt; So it seems it is a better time than ever to delve into version control. The old systems were plagued with various flaws and annoyances.  The latest systems resolve these issues, but I wouldn't know. I never used the older systems so I have no idea what the problems were. &lt;/p&gt; &lt;p&gt; Things have gone smoothly for me with &lt;a href="http://www.kernel.org/pub/software/scm/cogito/"&gt;Cogito&lt;/a&gt; (basically some shell scripts based around Linus Torvalds' &lt;a href="http://git.or.cz/"&gt;git&lt;/a&gt; tool). Even though it's designed to be a distributed source code management tool, it works well for one person editing a few files. &lt;/p&gt; &lt;p&gt; The only problem is that I can't easily host the repository on my Debian-stable server since git is too new, though &lt;a href="http://www.backports.org/"&gt;Debian backports&lt;/a&gt; exist. I could just use rsync, but this method is &lt;a href="http://www.gelato.unsw.edu.au/archives/git/0509/9695.html"&gt; deprecated and highly discouraged&lt;/a&gt;. [Also explained in &lt;a href="http://www.di.unipi.it/%7Escordino/sisop/git.html"&gt; this git tutorial&lt;/a&gt;; search for “rsync”.] &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-5829808474448482907?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/5829808474448482907/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=5829808474448482907' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5829808474448482907'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5829808474448482907'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2006/04/git-and-cogito_10.html' title='Git and Cogito'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-3442575057155368375</id><published>2006-03-27T01:00:00.001-08:00</published><updated>2011-04-06T01:49:02.534-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='games'/><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><category scheme='http://www.blogger.com/atom/ns#' term='puzzles'/><title type='text'>Mathematical Go</title><content type='html'>&lt;p&gt;I was taught to play &lt;a href="http://en.wikipedia.org/wiki/Go_%28board_game%29"&gt;go&lt;/a&gt; as a child, but it wasn't until a few years ago that I learned how complex the rules are, at least the ones used in tournaments. I had previously thought the rules were elegant and simple. &lt;/p&gt;&lt;p&gt;Actually, mathematicians have devised &lt;a href="http://brooklyngoclub.org/jc/rulesgo.html"&gt;elegant and simple rules of go&lt;/a&gt;. Unfortunately the mathematical rules are rarely used. Instead, there are &lt;a href="http://home.snafu.de/jasiek/bascomp.html"&gt; several popular rule sets&lt;/a&gt; with different properties. &lt;/p&gt;&lt;p&gt;Luckily in most games the complicated cases never arise, but it is irritating to know that in general, &lt;a href="http://io.debian.net/~tar/debian/nextgo/nextgo.app-3.0/FAQ"&gt; the outcome of the game can depend on the legality of suicide, and in many cases it is unclear whether a group is live or dead&lt;/a&gt; if the mathematical rules are not followed. &lt;/p&gt;&lt;p&gt;Evidently when the game was first invented, nobody thought about the messy corner cases. It seems as each one was discovered, an ad hoc solution was proposed, and over time these cumulative patches to the basic rules eroded the austere grace of the game. &lt;/p&gt;&lt;p&gt;As one might expect, starting afresh and approaching the game from a mathematician's point of view not only restores clarity and precision, but also yields unexpected results. For example, &lt;a href="http://www.amazon.com/gp/product/1568810326/"&gt;Berlekamp and Wolfe&lt;/a&gt; describe bizarre positions where highly nonintuitive moves are required to win. Even though such situations never occur in real play, studying them hints at the richness and depth of this ancient game. &lt;/p&gt;&lt;p&gt;Unsurprisingly, it was a mathematician who first drew my attention to the flaws in traditional go rule sets! &lt;/p&gt;&lt;p&gt;I am not sure why the mathematical rules have not caught on. Are they too difficult to implement in a tournament? Is the grip of tradition is too strong? Or is it simply that most in the go world are unfamiliar with these recent developments? &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-3442575057155368375?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/3442575057155368375/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=3442575057155368375' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3442575057155368375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3442575057155368375'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2006/03/mathematical-go_27.html' title='Mathematical Go'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-2209283692913247693</id><published>2006-03-26T01:00:00.001-08:00</published><updated>2011-04-06T01:49:02.540-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Blog Basics: Permalinks</title><content type='html'>&lt;p&gt; I wrote some scripts to add permalinks, which &lt;a href="http://en.wikipedia.org/wiki/Blog"&gt; a typical blog should have&lt;/a&gt;. I can understand why many people use dedicated blogging software, but I'm content to reinvent this wheel, as I'm comfortable with, and even enjoy, writing shell scripts. I also have more control this way. &lt;/p&gt; &lt;p&gt; I have one directory for every date I've posted. Inside there is one file per blog entry, containing an HTML snippet with the title in a &lt;tt&gt;h2&lt;/tt&gt; tag, and the date within a &lt;tt&gt;h3&lt;/tt&gt; tag. One script converts this to a page to be permalinked, while another pastes the entry into the main blog HTML files. &lt;/p&gt; &lt;p&gt; Inspired by &lt;a href="http://googleblog.blogspot.com/"&gt;the official Google blog&lt;/a&gt;, the permalink's filename is the title converted to lowercase with spaces converted to dashes, and all other characters removed. &lt;/p&gt; &lt;p&gt; I should eventually look into how the popular blogging software organizes things, but this scheme appears to work well. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-2209283692913247693?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/2209283692913247693/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=2209283692913247693' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2209283692913247693'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/2209283692913247693'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2006/03/blog-basics-permalinks_26.html' title='Blog Basics: Permalinks'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-4497948075209507962</id><published>2006-03-14T01:00:00.001-08:00</published><updated>2011-04-06T01:49:02.546-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Geometry Algorithms</title><content type='html'>&lt;p&gt; &lt;a href="http://en.wikipedia.org/wiki/Pi_day"&gt;Happy Pi Day!&lt;/a&gt; &lt;/p&gt; &lt;p&gt; Once in a while I visit &lt;a href="http://acm.uva.es/problemset/"&gt;ACM Programming Contest Problem Set Archive&lt;/a&gt;. Reading and trying out a problem or two reminds me of a time when I was eligible to compete. But mainly it makes me realize how little I knew back then, and how much I still can learn. For example, as mentioned in a previous post, I only came across the Dancing Links algorithm recently which comes in handy for several contest problems. &lt;/p&gt; &lt;p&gt; Many problems require simple &lt;a href="http://softsurfer.com/algorithm_archive.htm"&gt;geometry algorithms&lt;/a&gt;. Back in the day I thought I could derive what I needed on the fly for these sorts of problems. I still believe inventing algorithms is a good thing to do, but at some point one should learn what has been done before, as it is easy to miss clever and elegant optimizations. &lt;/p&gt; &lt;p&gt; For example, consider the simple problem of determining which side of a line a given point lies, where the line is defined by two given points. In the past, I would have naively computed the equation for the line and substituted the point in to see which side it lies on, but now I know that it is much easier to calculate &lt;a href="http://softsurfer.com/Archive/algorithm_0101/algorithm_0101.htm#Modern%20Triangles"&gt;the signed area&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-4497948075209507962?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/4497948075209507962/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=4497948075209507962' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4497948075209507962'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4497948075209507962'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2006/03/geometry-algorithms_14.html' title='Geometry Algorithms'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-4909354961055059579</id><published>2006-02-17T01:00:00.001-08:00</published><updated>2011-04-06T01:49:02.549-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Calculus Using Infinitesimals</title><content type='html'>&lt;p&gt; I only recently became aware that it is possible to formalize the methods that Newton and Leibniz first used when developing calculus. In fact, not only is it possible, but it is quite easy to understand, and the notation is more elegant than those of limits. &lt;/p&gt; &lt;p&gt; I had always looked down on the pseudo-proofs I was shown in physics lectures, handwavy sloppy arguments that defied the rigour and precision that centuries of mathematics fought for. I tolerated them because I needed the results. But it seems with a few definitions, you can have your cake and eat it. &lt;/p&gt; &lt;p&gt; One introduction to this subject is a &lt;a href="http://www.math.wisc.edu/%7Ekeisler/calc.html"&gt;free online book: “Elementary Calculus: An Approach Using Infinitesimals”&lt;/a&gt; by H. Jerome Keisler.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-4909354961055059579?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/4909354961055059579/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=4909354961055059579' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4909354961055059579'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4909354961055059579'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2006/02/calculus-using-infinitesimals_17.html' title='Calculus Using Infinitesimals'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-525502025400161612</id><published>2006-01-14T01:00:00.001-08:00</published><updated>2011-04-06T01:49:02.551-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Unicode on Debian GNU/Linux</title><content type='html'>&lt;p&gt; &lt;a href="http://www.unicode.org/"&gt;Unicode&lt;/a&gt; assigns each character a unique code [&lt;a href="http://en.wikipedia.org/wiki/Unicode"&gt;Wikipedia article&lt;/a&gt;]. My webserver now serves pages in UTF-8, and I have also switched my desktop to use Unicode. &lt;/p&gt; &lt;p&gt; There was no single compelling reason for this. Instead there were many minor advantages that together pushed me to adopt it. Small annoyances would arise. I wanted to add Chinese characters [&lt;a href="http://zhongwen.com/zi.htm"&gt;Chinese characters dictionary&lt;/a&gt;] [&lt;a href="http://www.pzlabs.com/soleri/"&gt;English-Chinese dictionary&lt;/a&gt;] to some of my pages, and also &lt;a href="http://www.cl.cam.ac.uk/%7Emgk25/ucs/quotes.html"&gt; Unicode curling quotes&lt;/a&gt; [ &lt;a href="http://www.dwheeler.com/essays/quotes-in-html.html"&gt;in HTML&lt;/a&gt;]. Some of the other characters look useful. Unicode is the rapidly becoming the dominant standard: even Windows uses it now. It is also far superior to the old way, i.e. dealing with &lt;a href="http://www.cs.tut.fi/%7Ejkorpela/chars.html"&gt;a myriad of mutually incompatible character encodings&lt;/a&gt;. &lt;a href="http://www.joelonsoftware.com/articles/Unicode.html"&gt; Every programmer ought to know about Unicode&lt;/a&gt;. &lt;/p&gt; &lt;p&gt; Migrating the server was easy. A minor tweak in a configuration file (&lt;tt&gt;AddDefaultCharset UTF-8&lt;/tt&gt; in &lt;tt&gt;srm.conf&lt;/tt&gt; for Apache), and changing XML headers was all that was needed. &lt;/p&gt; &lt;p&gt; The desktop side was more involved. For an in-depth explanation, see &lt;a href="http://melkor.dnp.fmph.uniba.sk/%7Egarabik/debian-utf8/HOWTO/howto.html"&gt; a step by step guide to switching Debian to UTF-8&lt;/a&gt; and &lt;a href="http://www.cl.cam.ac.uk/%7Emgk25/unicode.html"&gt;a guide for Unix in general&lt;/a&gt;. &lt;/p&gt; &lt;p&gt; First I reconfigured the Debian locales package (i.e. &lt;tt&gt;dpkg-reconfigure locales&lt;/tt&gt;) and selected &lt;tt&gt;en_AU.UTF-8&lt;/tt&gt;. &lt;/p&gt; &lt;p&gt; Next I checked if the programs I frequently use can handle UTF-8. Unfortunately for me, &lt;a href="http://www.eterm.org/"&gt;Eterm&lt;/a&gt; does not support Unicode. &lt;/p&gt; &lt;p&gt; After a brief investigation I settled on &lt;a href="http://software.schmorp.de/pkg/rxvt-unicode.html"&gt;rxvt-unicode (aka urxvt)&lt;/a&gt;. I miss some of the Eterm eyecandy, and it took me a while to supply command-line options to get urxvt looking like Eterm. On the other hand, urxvt is leaner, especially when run in client-server mode. &lt;/p&gt; &lt;p&gt; One minor inconvenience is that "TERM=rxvt-unicode" is not entrenched as it should be. For example, the dircolors supplied in Debian does not know about it, so either a custom file has to be written, or dircolors has to be fooled. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-525502025400161612?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/525502025400161612/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=525502025400161612' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/525502025400161612'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/525502025400161612'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2006/01/unicode-on-debian-gnulinux.html' title='Unicode on Debian GNU/Linux'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-7195892482916096224</id><published>2005-12-28T01:00:00.001-08:00</published><updated>2011-04-06T01:49:02.555-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='puzzles'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Solving Sudoku</title><content type='html'>I cobbled together two programs to solve Sudoku puzzles. One employs &lt;a href="http://www.angusj.com/sudoku/hints.php"&gt;tricks that people use&lt;/a&gt;, while the other is an implementation of Knuth's &lt;a href="http://en.wikipedia.org/wiki/Dancing_Links"&gt;Dancing Links algorithm as described at Wikipedia&lt;/a&gt;. It seems the first approach is sufficient for solving mainstream Sudokus, even without implementing all the tricks.&lt;ul&gt;&lt;li&gt;&lt;a href="http://cs.stanford.edu/~blynn/sudoku.git"&gt;http://cs.stanford.edu/~blynn/sudoku.git&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;Type &lt;tt&gt;git clone http://cs.stanford.edu/~blynn/sudoku.git&lt;/tt&gt; to fetch a copy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-7195892482916096224?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/7195892482916096224/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=7195892482916096224' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7195892482916096224'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7195892482916096224'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2005/12/solving-sudoku.html' title='Solving Sudoku'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-8364191878986649260</id><published>2005-12-12T01:00:00.001-08:00</published><updated>2011-04-06T01:49:02.558-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><category scheme='http://www.blogger.com/atom/ns#' term='puzzles'/><title type='text'>Quinapalus</title><content type='html'>Mark Owen aka Quinapalus is the author of the &lt;a href="http://benlynn.blogspot.com/2005/11/ajax-puzzle-firefox-extensions.html"&gt; number crossword I HTMLized (Ajaxed?) recently&lt;/a&gt;. &lt;a href="http://www.quinapalus.com/"&gt;His site&lt;/a&gt; contains many other puzzles, some of them also crossnumbers:&lt;br /&gt;&lt;ul&gt;&lt;li&gt; &lt;a href="http://www.quinapalus.com/threesquared.html"&gt;Three Squared&lt;/a&gt; &lt;/li&gt;&lt;li&gt; &lt;a href="http://www.quinapalus.com/twocubed.html"&gt;Two Cubed&lt;/a&gt; &lt;/li&gt;&lt;li&gt; &lt;a href="http://www.quinapalus.com/xtricate.html"&gt;Xtricate&lt;/a&gt; &lt;/li&gt;&lt;li&gt; &lt;a href="http://www.quinapalus.com/debased.html"&gt;Debased&lt;/a&gt; &lt;/li&gt;&lt;li&gt; &lt;a href="http://www.quinapalus.com/interstice.html"&gt;Interstice&lt;/a&gt; &lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-8364191878986649260?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/8364191878986649260/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=8364191878986649260' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/8364191878986649260'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/8364191878986649260'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2005/12/quinapalus_12.html' title='Quinapalus'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-6295004365179954590</id><published>2005-12-07T01:00:00.002-08:00</published><updated>2011-04-06T01:49:02.561-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>HTML Preprocessing</title><content type='html'>I replaced the PHP in &lt;a href="http://wingchun.stanford.edu/"&gt;wingchun.stanford.edu&lt;/a&gt; with static HTML. To avoid repeating code I used &lt;a href="http://www.nothingisreal.com/gpp/"&gt;GPP&lt;/a&gt;, a generic&lt;br /&gt;preprocessor.&lt;br /&gt;&lt;br /&gt;I initially selected PHP for all my webpages because I had grandiose plans to build exciting dynamic sites. It never happened, but I kept the PHP in case I came up with some ideas. The development cycle is also one step shorter, and I don't get enough traffic for the performance hit to matter.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-6295004365179954590?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/6295004365179954590/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=6295004365179954590' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/6295004365179954590'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/6295004365179954590'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2005/12/html-preprocessing.html' title='HTML Preprocessing'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-4779181676656710380</id><published>2005-12-06T01:00:00.001-08:00</published><updated>2011-04-06T01:49:02.564-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Disabling Links to the Current Page</title><content type='html'>According to Jakob Nielsen's list of &lt;a href="http://www.useit.com/alertbox/20031110.html"&gt;The Ten Most Violated Homepage Design Guidelines&lt;/a&gt;, a link should never point to the current page.&lt;br /&gt;&lt;br /&gt;I've seen high-profile sites that break this rule so I'd expect most users not to get too confused. Nevertheless, I whipped up &lt;a href="http://cs.stanford.edu/%7Eblynn/20051206/disable_selflink.js"&gt;some javascript&lt;/a&gt; to do disable links to the current page for this site. Ideally I'd like the links to be removed in the raw HTML and not require Javascript, but the way I've built my site makes this difficult.&lt;br /&gt;&lt;br /&gt;A brief search revealed that some people do this by changing the onclick callback, and also the mouse cursor, but I prefer to replace the whole anchor tag with a span tag of a particular class.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-4779181676656710380?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/4779181676656710380/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=4779181676656710380' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4779181676656710380'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4779181676656710380'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2005/12/disabling-links-to-current-page_06.html' title='Disabling Links to the Current Page'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-7120121010486295015</id><published>2005-11-21T01:00:00.001-08:00</published><updated>2011-04-06T01:49:02.570-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><category scheme='http://www.blogger.com/atom/ns#' term='puzzles'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Ajax Puzzle, Firefox Extensions</title><content type='html'>Everyone seems to be talking about &lt;a href="http://www.mygadgetbag.com/MGBResearch/MGBResearchArticles/tabid/261/articleType/ArticleView/articleId/445/The-Ten-Best-Ajax-Links-Tutorials-Examples-and-History.aspx"&gt;Ajax&lt;/a&gt; these days. By chance I was recently forced to learn Javascript, so I threw together a page that may count as using Ajax.&lt;br /&gt;&lt;br /&gt;My page is &lt;a href="http://cs.stanford.edu/%7Eblynn/20051121/interstice.html"&gt;a number crossword&lt;/a&gt; I encountered years ago. It might be too simple to be considered Ajax, but it is similar to the puzzles at &lt;a href="http://www.number-logic.com/"&gt;Number-Logic.com&lt;/a&gt;, which do use Ajax. Anyway, dropping the word "Ajax" should get me a few extra hits at least!&lt;br /&gt;&lt;br /&gt;I only checked the page in Firefox. It may look weird in other browsers. Speaking of Firefox, I like this list: &lt;a href="http://db.rambleschmack.net/pc_tips/best_firefox_extensions"&gt;Best Firefox Extensions&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And speaking of my favourite number puzzles, I'm fond of the "Alice bit the white rabbit" &lt;a href="http://en.wikipedia.org/wiki/Cryptarithm"&gt;cryptarithm&lt;/a&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; A L I C E +&lt;br /&gt;     B I T&lt;br /&gt;     T H E&lt;br /&gt;&lt;span style="text-decoration: underline;"&gt;  W H I T E&lt;/span&gt;&lt;br /&gt;R A B B I T&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I haven't been able to track down the source of this gem.&lt;br /&gt;&lt;br /&gt;I renamed this page from "Insert Blog Name Here" to the &lt;a href="http://en.wikipedia.org/wiki/Backronym"&gt;backronym&lt;/a&gt; BL:OG. The "Openly Geeky" phrase was supposed to be a play on the words "openly gay", but they have more in common than I first thought: declaring yourself as either one discourages the opposite sex from pursuing you!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-7120121010486295015?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/7120121010486295015/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=7120121010486295015' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7120121010486295015'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7120121010486295015'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2005/11/ajax-puzzle-firefox-extensions_21.html' title='Ajax Puzzle, Firefox Extensions'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-5950152120895877429</id><published>2005-11-02T01:00:00.001-08:00</published><updated>2011-04-06T01:49:02.576-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tricks'/><category scheme='http://www.blogger.com/atom/ns#' term='maths'/><title type='text'>Party Tricks</title><content type='html'>This page is developing a life of its own. It's getting updated frequently with useless random information. I'm embracing the inevitable and intend to make it look like what it has become: a blog.&lt;br /&gt;&lt;br /&gt;Geeky party tricks:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://rudy.ca/doomsday.html"&gt;The Doomsday Algorithm&lt;/a&gt;[&lt;a href="http://en.wikipedia.org/wiki/Doomsday_algorithm"&gt;Wikipedia article&lt;/a&gt;]: calculate the day of the week for any date instantly.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.1729.com/blog/CubeRoots.html"&gt;Do Cube Roots of 9-digit Numbers in Your Head&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-5950152120895877429?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/5950152120895877429/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=5950152120895877429' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5950152120895877429'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5950152120895877429'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2005/11/party-tricks_02.html' title='Party Tricks'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-7519758178045866526</id><published>2005-11-01T03:00:00.001-08:00</published><updated>2011-04-06T01:49:02.582-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Elements of Style</title><content type='html'>I had heard of William Strunk's &lt;i&gt;Elements of Style&lt;/i&gt; long ago. I finally saw what all the fuss is about when I found this &lt;!--defunct a href="http://sut1.sut.ac.th/strunk/"--&gt;&lt;a href="http://www.crockford.com/wrrrld/style.html"&gt;online HTML version of the book&lt;/a&gt; [&lt;a href="http://www.bartleby.com/141/"&gt;alternate link&lt;/a&gt;].&lt;br /&gt;&lt;br /&gt;Much of this 1918 work is still relevant today. I was surprised to find that many misused and/or hackneyed words and phrases have been around for many years.&lt;br /&gt;&lt;br /&gt;When I write English, I borrow techniques I use to write code. I start with a syntactically correct body of text that gets the job done. I then revise and optimize: I reword, rephrase, replace and remove lines, aiming for clean, concise, efficient and effective writing.&lt;br /&gt;&lt;br /&gt;The analogy isn't perfect. With prose, I'll throw in extra words if I like the way it sounds, but for code, I'll sacrifice style for performance.&lt;br /&gt;&lt;br /&gt;Some of the rules in &lt;i&gt;Elements of Style&lt;/i&gt; seem to achieve the same goals, though they are explained from a nonprogrammer's viewpoint!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-7519758178045866526?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/7519758178045866526/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=7519758178045866526' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7519758178045866526'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/7519758178045866526'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2005/11/elements-of-style_01.html' title='Elements of Style'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-8836482711715164878</id><published>2005-11-01T02:00:00.001-08:00</published><updated>2011-04-06T01:49:02.584-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tricks'/><category scheme='http://www.blogger.com/atom/ns#' term='puzzles'/><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>Drums, Engines, Sudoku, Poker Chips, Dodecahedrons</title><content type='html'>Some links I don't want to forget:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://www.kembrashear.com/"&gt;Drumkit&lt;/a&gt; implemented in Flash&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.keveney.com/Engines.html"&gt;Animated Engines&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.number-logic.com/"&gt;Sudoku at Number-Logic.com&lt;/a&gt;: I've lost a lot of time here recently. I only recently discovered these frustrating puzzles, but according to the &lt;a href="http://en.wikipedia.org/wiki/Sudoku"&gt;Wikipedia entry on Sudoku&lt;/a&gt;, they've been around since 1979.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.21ace.com/poker_chip_tricks.html"&gt;Poker chip tricks&lt;/a&gt; have also been eating up my time of late. Note there are &lt;a href="http://pokerchiptricks.com/poker-chip-tricks/"&gt;different ways to do poker chips tricks&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.ii.uib.no/%7Earntzen/kalender/"&gt;12-sided calendar&lt;/a&gt;: print and construct your very own dodecahedral calendar.&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-8836482711715164878?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/8836482711715164878/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=8836482711715164878' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/8836482711715164878'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/8836482711715164878'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2005/11/drums-engines-sudoku-poker-chips_01.html' title='Drums, Engines, Sudoku, Poker Chips, Dodecahedrons'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-8044344594397911741</id><published>2005-11-01T01:00:00.001-08:00</published><updated>2011-04-06T01:49:02.587-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Web Authoring Notes</title><content type='html'>&lt;a href="http://cs.stanford.edu/~blynn/web/"&gt;My notes on creating web sites&lt;/a&gt; are slipshod and haphazard. This would be fine since I wrote them for myself, but it's getting to a point where even I'm having trouble wading through the chaos to find information I want.&lt;br /&gt;&lt;br /&gt;I've started reorganizing that part of my site. Some of the material&lt;br /&gt;is more suitable for a blog than a reference.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-8044344594397911741?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/8044344594397911741/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=8044344594397911741' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/8044344594397911741'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/8044344594397911741'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2005/11/web-authoring-notes_01.html' title='Web Authoring Notes'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-6080631684800149661</id><published>2005-10-20T01:00:00.001-07:00</published><updated>2011-04-06T01:49:02.592-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>HTML Notes</title><content type='html'>I switched the main website from HTML 4.01 Transitional to Strict. I can't remember why I chose to use transitional in the first place. The only deprecated HTML I had was an align attribute in an image tag.&lt;br /&gt;&lt;br /&gt;Students in the &lt;a href="http://www.stanford.edu/class/cs105/"&gt;CS105 class&lt;/a&gt; I'm TAing have asked about indenting using straight HTML. This reminded me of &lt;a href="http://www.blooberry.com/indexdot/html/topics/indent.htm"&gt;a page describing hacks to indent using HTML&lt;/a&gt;. (I'd use CSS to do it, but we don't expect them to know CSS well.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-6080631684800149661?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/6080631684800149661/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=6080631684800149661' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/6080631684800149661'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/6080631684800149661'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2005/10/html-notes_20.html' title='HTML Notes'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-3777863429576373112</id><published>2005-10-15T01:00:00.001-07:00</published><updated>2011-04-06T01:49:02.598-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='puzzles'/><title type='text'>Rubik's Revenge</title><content type='html'>I bought a &lt;a href="http://en.wikipedia.org/wiki/Rubik%27s_Revenge"&gt;Rubik's Revenge&lt;/a&gt; (a 4x4x4 Rubik's Cube) a long time ago. I did figure out a solution, but it's extremely slow. The 3x3x3 method I learnt when I was a kid is also fairly inefficient.&lt;br /&gt;&lt;br /&gt;I was curious how people speed things up for both types of cubes. After browsing for a bit, I've decided the approach best for me is:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Reduce to 3x3x3 case as described by &lt;a href="http://www.speedcubing.com/chris/4speedsolve.html"&gt;Chris Hardwick&lt;/a&gt;. Now standard Rubik's Cube algorithms can be used.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.necrophagous.co.uk/cubestation/cross/cross.php"&gt;Solve the cross&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.necrophagous.co.uk/cubestation/f2l/f2l.php"&gt;Solve the first two layers&lt;/a&gt; except for one corner piece. Originally I had planned to follow the &lt;a href="http://www.zborowski.republika.pl/expert3x3x3method.html"&gt;"ZB" system&lt;/a&gt;, which has you solve the first two layers except for one corner piece and one adjacent edge piece, but this leads to numerous cases for step 2 of the system. For now, I'd rather sacrifice a few moves to get one more edge piece in place to cut down the number of cases for the next step.&lt;br /&gt;  &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Perform step 2 of the ZB system to put the last first-layer corner piece in place while at the same time orienting the last-layer edges. They are &lt;a href="http://www.cubezone.be/zbf2l.html"&gt;the last few cases on this page&lt;/a&gt;. (I don't think I could ever memorize the entire ZB system. I lose a few turns putting the last middle-layer edge in place, but I only have to learn a small fraction of the cases.)&lt;br /&gt;  &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Either &lt;a href="http://www.math.leidenuniv.nl/%7Ejnoort/index.php?location=tutorial4"&gt;orient the last layer&lt;/a&gt; ("OLL"), and then &lt;a href="http://www.ws.binghamton.edu/fridrich/Mike/permute.html"&gt;permute the last layer&lt;/a&gt; ("PLL"), or for more efficiency, &lt;a href="http://www.ai.univ-paris8.fr/%7Ebh/cube/solutions_567.html"&gt;achieve both goals simultaneously&lt;/a&gt;.&lt;/li&gt;&lt;/ol&gt;One of the above sites also points to &lt;a href="http://www.superhandz.com/"&gt;a site with videos of people posessing an amazing amount of digital dexterity&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-3777863429576373112?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/3777863429576373112/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=3777863429576373112' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3777863429576373112'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3777863429576373112'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2005/10/rubik-revenge.html' title='Rubik&amp;#39;s Revenge'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-3189630615308528877</id><published>2005-09-30T01:00:00.001-07:00</published><updated>2011-04-06T01:49:02.604-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Virtual Hosting</title><content type='html'>I finally added the following to Apache's &lt;tt&gt;srm.conf&lt;/tt&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;NameVirtualHost *&lt;br /&gt;&lt;br /&gt;&amp;lt;VirtualHost *&amp;gt;&lt;br /&gt;ServerName wingchun.stanford.edu&lt;br /&gt;DocumentRoot /var/www/wingchun&lt;br /&gt;&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;VirtualHost *&amp;gt;&lt;br /&gt;ServerName rooster.stanford.edu&lt;br /&gt;DocumentRoot /var/www&lt;br /&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/code&gt;&lt;/blockquote&gt;so now &lt;tt&gt;rooster.stanford.edu&lt;/tt&gt; and &lt;tt&gt;wingchun.stanford.edu&lt;/tt&gt; look like different sites. However, for reasons I don't understand yet, in order to get SSL working on my whole site, I had to add an extra &lt;tt&gt;DocumentRoot&lt;/tt&gt; directive to the &lt;tt&gt;VirtualHost _default_:443&lt;/tt&gt; section needed by &lt;tt&gt;mod_ssl&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;I reinstalled &lt;a href="http://www.webdav.org/mod_dav/"&gt;the &lt;tt&gt;mod_dav&lt;/tt&gt; Apache module&lt;/a&gt;, this time to play with some calendar and bookmark applications.  I used to have it running a long time ago (see &lt;a href="http://toolbar.netcraft.com/site_report?url=http://rooster.stanford.edu"&gt; Netcraft's history of this site&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Recently I've been exploring &lt;a href="http://del.icio.us/popular/"&gt;a frequently-changing list of interesting bookmarks&lt;/a&gt;, containing (among other things) links to ephemerally popular pages like &lt;a href="http://www.livejournal.com/users/jwz/543178.html"&gt;how to turn your hamster into a fighting machine&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-3189630615308528877?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/3189630615308528877/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=3189630615308528877' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3189630615308528877'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/3189630615308528877'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2005/09/virtual-hosting_30.html' title='Virtual Hosting'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-5460025328957243555</id><published>2005-04-17T01:00:00.002-07:00</published><updated>2011-04-06T01:49:02.607-07:00</updated><title type='text'>Wasting Time</title><content type='html'>Instead of wasting time on my homepage, I recently wasted some time fooling around with &lt;a href="https://addons.update.mozilla.org/extensions/?application=firefox"&gt;Firefox extensions&lt;/a&gt;. Up until now the only extension I used on a regular basis was &lt;a href="https://addons.update.mozilla.org/extensions/moreinfo.php?application=firefox&amp;amp;numpg=10&amp;amp;id=12"&gt;All-in-One Gestures&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;But now I've tried ForecastFox and Adblock (both are in the &lt;a href="https://addons.update.mozilla.org/extensions/showlist.php?application=firefox&amp;amp;numpg=10&amp;amp;category=Popular"&gt;list of popular extensions&lt;/a&gt;, and I like what I see. I stumbled upon this &lt;a href="http://www.geocities.com/pierceive/adblock/"&gt;list of ads to block&lt;/a&gt; which seems to be frequently updated and effective. I started using Flashblock in concert with Adblock.&lt;br /&gt;&lt;br /&gt;To top it off I procrastinated further by visiting &lt;a href="http://www.pointlesssites.com/"&gt;pointless sites&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-5460025328957243555?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/5460025328957243555/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=5460025328957243555' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5460025328957243555'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/5460025328957243555'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2005/04/wasting-time_7786.html' title='Wasting Time'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-4727328988708638065</id><published>2005-04-12T01:00:00.002-07:00</published><updated>2011-04-06T01:49:02.610-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>The Obsession Fades</title><content type='html'>At last I'm starting to come to my senses. For a while I was spending almost all of my free time on my site. I created a &lt;a href="http://en.wikipedia.org/wiki/Cascading_Style_Sheets"&gt;Wikipedia-like CSS&lt;/a&gt; for some of pages, tweaked other CSS files, added links from pages (from my old site that are no longer accessible, and also from my bookmarks), and reorganized a little.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-4727328988708638065?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/4727328988708638065/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=4727328988708638065' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4727328988708638065'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4727328988708638065'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2005/04/obsession-fades_6050.html' title='The Obsession Fades'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-4413531601997860560</id><published>2005-04-10T01:00:00.002-07:00</published><updated>2011-04-06T01:49:02.615-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>More Redesign</title><content type='html'>Less PHP, CSS code duplication throughout site. Changed the menu style. No more submenus, and now there are unclickable headings. Found a nice &lt;a href="http://wellstyled.com/tools/colorscheme2/index-en.html"&gt;colour scheme generation tool&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I shrunk the validation buttons as I felt they were getting in the way. Forgot to handle the alpha channel the first time around. They now sit humbly in the bottom right hand corner.&lt;br /&gt;&lt;br /&gt;I experimented with a pink/purple colour scheme with a horizontal menu for the &lt;a href="http://cs.stanford.edu/%7Eblynn/bliss/"&gt;Bliss pages&lt;/a&gt;. Most of my site is back up, though I got rid of some of the old content. The xmmspipe site is the only one left with the original look-and-feel (not counting the &lt;a href="http://cs.stanford.edu/%7Eblynn/toshiba/"&gt;old Toshiba pages&lt;/a&gt;), though my mathematics notes still look the same as when I first started using CSS.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-4413531601997860560?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/4413531601997860560/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=4413531601997860560' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4413531601997860560'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4413531601997860560'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2005/04/more-redesign_4881.html' title='More Redesign'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4222267598459829544.post-4998949961583219080</id><published>2005-04-09T01:00:00.002-07:00</published><updated>2011-04-06T01:49:02.620-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Major Homepage Renovation</title><content type='html'>When I first wrote my homepage, I modeled my site on &lt;a href="http://www.debian.org/"&gt;Debian's site&lt;/a&gt; which at the time used HTML tables for layout. I had never used CSS and I also felt that plain HTML would give maximum exposure.&lt;br /&gt;&lt;br /&gt;A while later, I had to learn CSS when I wrote pages using MathML.  The XHTML/CSS approach seemed cleaner and easier, and sometimes when I added new sections to my site I'd use XHTML and CSS. But I never converted the rest of my site out of laziness, and also I was worried that my site would look wrong in IE with its numerous CSS bugs.&lt;br /&gt;&lt;br /&gt;I now feel the time is right to use CSS on my whole site [&lt;a href="http://phrogz.net/CSS/WhyTablesAreBadForLayout.html"&gt;10 reasons why tables should not be used for layout&lt;/a&gt;].  I've spent a while trying to learn CSS properly. Additionally I've been reading about webpage design. Apparently, sans-serif fonts are preferred to serif fonts when on screen. The best widely available on-screen sans-serif font is Verdana while for serif fonts it's Georgia[&lt;a href="http://www.webdesignfromscratch.com/readability.cfm"&gt;1&lt;/a&gt;, &lt;a href="http://www.buildwebsite4u.com/building/web-fonts.shtml"&gt;2&lt;/a&gt;].&lt;br /&gt;&lt;br /&gt;Note I'm still sticking to HTML for now, as XHTML is not handled properly by IE or Google yet.&lt;br /&gt;&lt;br /&gt;Now my default CSS file gives my site a kind of bluish &lt;a href="http://slashdot.org/"&gt;Slashdot.org&lt;/a&gt; look. My only regret is that &lt;a href="http://w3m.sourceforge.net/"&gt;w3m&lt;/a&gt; will no longer be able to render my page layout (perhaps that's the only advantage of using tables for layout?), but then again, how often do I use text mode browsers?&lt;br /&gt;&lt;br /&gt;My site is also long overdue for a general cleanup. So far I've been working on the presentation of my page but the content will also be reorganized.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4222267598459829544-4998949961583219080?l=benlynn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://benlynn.blogspot.com/feeds/4998949961583219080/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4222267598459829544&amp;postID=4998949961583219080' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4998949961583219080'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4222267598459829544/posts/default/4998949961583219080'/><link rel='alternate' type='text/html' href='http://benlynn.blogspot.com/2005/04/major-homepage-renovation_09.html' title='Major Homepage Renovation'/><author><name>Ben Lynn</name><uri>http://www.blogger.com/profile/09117417699962852340</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
