Sat, 28 Aug 2010

I’d like to claim this is “one line of C”, but actually it’s 98
characters, which is more than a regulation 80-character punched card.

    char*s;main(int c,char**v){for(;;)for(s=v[1],puts(s),c=0;s[2];*s++=".##.###."[c&7],c=s[1]&1|c*2);}

It computes successive states of rule 110; it works like this:

    : kragen at inexorable:~/devel/inexorable-misc ; ./rule110  ................................##........................... | head -20
    ................................##...........................
    ...............................#.##..........................
    ..............................###.##.........................
    .............................#..##.##........................
    ............................####.##.##.......................
    ...........................#...##.##.##......................
    ..........................###.#.##.##.##.....................
    .........................#..####.##.##.##....................
    ........................####...##.##.##.##...................
    .......................#...##.#.##.##.##.##..................
    ......................###.#.####.##.##.##.##.................
    .....................#..####...##.##.##.##.##................
    ....................####...##.#.##.##.##.##.##...............
    ...................#...##.#.####.##.##.##.##.##..............
    ..................###.#.####...##.##.##.##.##.##.............
    .................#..####...##.#.##.##.##.##.##.##............
    ................####...##.#.####.##.##.##.##.##.##...........
    ...............#...##.#.####...##.##.##.##.##.##.##..........
    ..............###.#.####...##.#.##.##.##.##.##.##.##.........
    .............#..####...##.#.####.##.##.##.##.##.##.##........

If you try to run it without a command-line argument, it will crash.

As you can probably guess, both the state transition function and the
graphical output are encoded in the `".##.###."` string. If you
replace it with `v[2]`, then you can supply your own state transition
function. If you decide to use other characters for the display, make
sure that their low-order bits are different.

This software is available via

    git clone http://canonical.org/~kragen/sw/inexorable-misc.git

(or in <http://canonical.org/~kragen/sw/inexorable-misc>) in the file
`rule110.c`.

Like everything else posted to kragen-hacks without a notice to the
contrary, this software is in the public domain.

Sat, 21 Aug 2010

    #!/bin/sh
    set -e                          # exit on errors
    # KDE installs my custom keymap when I log in, but if my keyboard
    # crashes and reboots — or gets unplugged and plugged back in — it
    # resets to the default keymap. So I wrote this script to restore my
    # custom keymap within a second if that happens. This is almost
    # certainly the wrong way to solve the problem, but it's pretty easy.

    while : ; do
        # My default keymap has no Multi_key, but my custom keymap does
        # (on keycode 108), which is how you can tell the difference. 
        if ! xmodmap -pk | grep -q Multi_key; then
            xmodmap ~/.Xmodmap
        fi
        sleep 1
    done

(End of `xmodmapd`.)


This software is available via

    git clone http://canonical.org/~kragen/sw/inexorable-misc.git

(or in <http://canonical.org/~kragen/sw/inexorable-misc>) in the file
`xmodmapd`.

Like everything else posted to kragen-hacks without a notice to the
contrary, this software is in the public domain.

Sat, 14 Aug 2010

I don’t know shucks about statistics, but I thought I would toy with
some different random distributions to see how they behaved. Contrary
to my expectations, the sample median was never the more consistent
measure, and was often worse.

#!/usr/bin/python
"""Different measures of central tendency have different variability.

And it depends on the distribution of the underlying data.

Some distributions are easy to characterize from a random sample.  If
your data is normally or uniformly distributed, you can get a pretty
good estimate of the mean, which is also the median, after just ten or
twenty data points.

But some other distributions are not so well-behaved. The exponential
distribution is a common one. Its median is well to the left of its
mean.  Does the sample mean or the sample median have greater
variance? I hypothesize, without actually doing the math, that the
sample mean of an exponential distribution has proportionally greater
variance, and therefore the sample median is a better measure to use,
if you have to pick one.

"""

from __future__ import division
import random, math, sys

sample_mean = lambda sample: sum(sample)/len(sample)
sample_means = lambda samples: map(sample_mean, samples)

# wrong for even samples, but close enough:
sample_median = lambda sample: sorted(sample)[len(sample)//2] 
sample_medians = lambda samples: map(sample_median, samples)

uniform_sample = lambda n: [random.uniform(0, 1) for ii in range(n)]
expo_sample = lambda n: [random.expovariate(1) for ii in range(n)]

def standard_deviation(sample):
    mean = sample_mean(sample)
    return math.sqrt(sum((x - mean)**2 for x in sample)/(len(sample)-1))

uniform_samples = lambda n, m: [uniform_sample(m) for ii in range(n)]
expo_samples = lambda n, m: [expo_sample(m) for ii in range(n)]

def compare(n, m):
    print "%d samples of %d items each:" % (n, m)
    print "Uniform:",
    describe(uniform_samples(n, m))
    print "Exponential:",
    describe(expo_samples(n, m))

def describe(samples):
    means, medians = sample_means(samples), sample_medians(samples)
    
    print "standard deviation of mean %.2f (mean mean %.2f), of median %.2f (mean median %.2f)" % (standard_deviation(means), sample_mean(means),
                                                                                                   standard_deviation(medians), sample_mean(medians))

if __name__ == '__main__':
    compare(int(sys.argv[1]), int(sys.argv[2]))

(End of `variance.py`.)

Example usage:

    : kragen at inexorable:~/devel/inexorable-misc ; ./variance.py 10000 20
    10000 samples of 20 items each:
    Uniform: standard deviation of mean 0.06 (mean mean 0.50), of median 0.11 (mean median 0.52)
    Exponential: standard deviation of mean 0.22 (mean mean 1.00), of median 0.24 (mean median 0.77)

This software is available via

    git clone http://canonical.org/~kragen/sw/inexorable-misc.git

(or in <http://canonical.org/~kragen/sw/inexorable-misc>) in the file
`variance.py`.

Like everything else posted to kragen-hacks without a notice to the
contrary, this software is in the public domain.

Sat, 07 Aug 2010

I was curious where my time was going, so I programmed my computer to
ask me, “What are you doing right now?” in a popup window about four
times an hour. It logs the answers for my later perusal. Apparently I
read a lot and don’t have sex a very large fraction of the time.

I tried porting this to GTK with the Python GTK binding, which has
better fonts and keybindings, but I couldn’t figure out how to get the
appropriately annoying focus-stealing and pop-up-on-top behavior.

#!/usr/bin/python
# -*- coding: utf-8; -*-
"""Asks, "What are you doing right now?" at random, averaging every 15 minutes.

Results are appended to `~/waydrn15.log` by default.

"""

import sys, os, Tkinter, time, random


### Tkinter interface

# This is because Tkinter makes it unnecessarily difficult to create
# and configure Tk widgets (unless you import a zillion names into
# your own namespace).

class TkinterWrapper:
    "Small wrapper to make it easier to create Tk subwidgets."

    def __init__(self, underlying_widget):
        self.underlying_widget = underlying_widget

    def __getattr__(self, name):
        "Transparently pass on attribute requests other than for subwidgets."
        return getattr(self.underlying_widget, name)

def _attach_subwidget_method(name):
    "Create a method on TkinterWrapper to create a kind of subwidget."
    widget_class = getattr(Tkinter, name)
    def subwidget_method(self, *args, **kwargs):
        return TkinterWrapper(widget_class(self.underlying_widget, *args,
                                           **kwargs))
    setattr(TkinterWrapper, name, subwidget_method)

# These are the subwidget types I’m currently using.
for widget_class_name in 'Label Entry Button Frame'.split():
    _attach_subwidget_method(widget_class_name)


def Tk(*args, **kwargs):
    "Wrapper for Tkinter.Tk."
    return TkinterWrapper(Tkinter.Tk(*args, **kwargs))
def Toplevel(*args, **kwargs):
    "Wrapper for Tkinter.Toplevel."
    return TkinterWrapper(Tkinter.Toplevel(*args, **kwargs))

def pack(*args, **kwargs):
    "A pack function like Tk’s pack command, which takes multiple widgets."
    for widget in args:
        widget.pack(**kwargs)


### Application code

def append_to_file(filename, astring):
    "Append the given string to the file with the given name."
    f = open(filename, 'a')

    try:
        f.write(astring)
    finally:
        f.close()

class QueryWindow:
    "The window that pops up to ask you what you’re doing."
    def __init__(self, filename, windows):
        """Instantiate a QueryWindow object, but don’t open the window.

        filename: the filename to log to.
        windows: the shared list of windows.

        You can't call any other methods on the QueryWindow until you
        call `.open()`.

        """
        self.filename = filename
        self.windows = windows
        windows.append(self)

        now = time.localtime(time.time())
        self.datetime = time.strftime('%Y-%m-%d %H:%M', now)
        self.text = ('What are you doing right now? (%s)' %
                     time.strftime('%H:%M', now))

    def open(self):
        "Open the window on the display."
        self.toplevel = Toplevel()

        self.entry = self.toplevel.Entry(width=80)
        self.entry.bind('<Return>', self.click)

        buttons = self.toplevel.Frame()
        self.button = buttons.Button(text='Log it for this time',
                                     command=self.click)
        self.button.bind('<Return>', self.click)

        self.allbutton = buttons.Button(text='Log for all',
                                        command=self.log_all)
        self.allbutton.bind('<Return>', self.log_all)

        pack(self.button, self.allbutton, side='left')
        pack(self.toplevel.Label(text=self.text), self.entry, buttons)

        self.entry.focus()
        self.toplevel.lift()      # raise above other windows, stealing focus

    def message(self):
        "Returns the message the user has typed."
        return self.entry.get().encode('utf-8')

    # *args because .bind passes an event argument, while a button
    # command doesn’t.
    def click(self, *args):
        "Event handler that logs the user's message and closes the window."
        self.close_with(self.message())

    def log_all(self, *args):
        "Logs the user’s message for all open windows and closes them."
        # Save the message, since we can’t call self.message() after
        # the window closes.
        message = self.message()

        # Copy the list of windows since it will be changed as we
        # iterate over it and each window removes itself.
        for window in self.windows[:]:
            window.close_with(message)

    def close_with(self, message):
        "Logs the specified message and closes the window."
        if message == '':      # ignore attempts to log empty messages
            return
        append_to_file(self.filename, '%s %s\n' % (self.datetime, message))
        self.windows.remove(self)
        self.toplevel.destroy()

class PoissonScheduler:
    "A scheduler object that schedules itself with Tk to run a thunk randomly."
    def __init__(self, mainwin, seconds, thunk):
        """Instantiate the scheduler, but don’t start it running.

        mainwin: the Tk widget to use for scheduling (say, a Tk())
        seconds: the average interval between random runs of the thunk.
        thunk: the code to run periodically.

        """
        self.mainwin = mainwin
        self.seconds = seconds
        self.thunk = thunk

    def run(self):
        # Generate an exponential-variate delay in order to produce a
        # Poisson process, i.e. the probability of a window popping up
        # in any particular time interval is independent of when the
        # last one popped up.
        seconds = random.expovariate(1.0/self.seconds)
        self.mainwin.after(int(1000 * seconds), self.fire)

    def fire(self):
        self.thunk()
        self.run()

def main(argv):
    if len(argv) == 2:
        filename = argv[1]
    else:
        filename = os.path.join(os.environ['HOME'], 'waydrn15.log')
    
    mainwin = Tk()
    mainwin.title('time sampling applet')
    pack(mainwin.Label(text='Close this window to stop logging your time.'),
         mainwin.Label(text='Logging to %r.' % filename))

    windows = []
    seconds = 15*60
    # uncomment for debugging: seconds = 5
    scheduler = PoissonScheduler(mainwin, seconds,
                                 lambda: QueryWindow(filename, windows).open())
    scheduler.run()
    mainwin.mainloop()

if __name__ == '__main__':
    main(sys.argv)

# Local Variables:
# compile-command: "./waydrn15.py"
# End:

(End of `waydrn15.py`.)

This software is available via

    git clone http://canonical.org/~kragen/sw/inexorable-misc.git

(or in <http://canonical.org/~kragen/sw/inexorable-misc>) in the file
`waydrn15.py`.

Like everything else posted to kragen-hacks without a notice to the
contrary, this software is in the public domain.

Sat, 31 Jul 2010

This one you can run at <http://canonical.org/~kragen/sw/inexorable-misc/triangle.html>.

It’s just a very simple widget built using jQuery UI, with a slider
and a text box. I’d like to get it to lay out nicely, but it doesn’t.

(Also, Beatrice and I were up late one night solving the equations to
figure out how to do this. I guess I ought to have remembered, but I
couldn’t.)

    <html><head>
      <title>A triangle calculator with jQuery UI and &lt;canvas&gt;</title>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <!--  <link rel="stylesheet" href="http://www.canonical.org/~kragen/style.css" /> -->
      <link type="text/css" href="jquery-ui/themes/base/ui.all.css" rel="stylesheet" />
    </head><body>
      <h1>A triangle calculator with jQuery UI and &lt;canvas&gt;</h1>
      <canvas id="triangle" width="300" height="300"></canvas>

      <div class="slider_with_display" id="a">A: </div>
      <div class="slider_with_display" id="b">B: </div>
      <p>(Calculated: <span class="calcb"></span>)</p>
      <div class="slider_with_display" id="c">C: </div>
      <p>(Calculated: <span class="calcc"></span>)</p>

      <p>Intersection coordinates: <span class="coords"></span></p>

      <script src="jquery-ui/jquery-1.3.2.js"></script>
      <script src="jquery-ui/ui/ui.core.js"></script>
      <script src="jquery-ui/ui/ui.slider.js"></script>
      <script type="text/javascript">
        me = {a: 30, b: 30, c: 30};

        $.widget("ui.slider_with_display", {
          _init: function() {
            var that = this;
            // We use an <input> so that we can change its value
            // efficiently with .val().
            var display = $('<input size="3"/>')
             .css({fontFamily: 'inherit', fontSize: 'inherit'})
             .keyup(function() { that.value(this.value) }); // permit editing by hand
            var update = function(event, ui) { display.val(ui.value); that._trigger('change', event, ui); };
            var slider = $('<div/>').slider({ step: this.options.step, slide: update, change: update });
            this.slider = slider;
            this.element.append(display).append(slider);
          },

          value: function(newValue) {
            return this.slider.slider('value', newValue);
          }
        });

        $.extend($.ui.slider_with_display, {
          defaults: { step: 10 }
        });

        // find coordinates of bc corner of triangle, assuming ab is at
        // (0, 0) and ac is at (0, a)
        me.triangle_corner_coordinates = function(a, b, c) {
          var bsq = b*b;
          var x = (bsq - c*c) / (2.0 * a) + a / 2.0;
          var xsq = x*x;
          return (xsq < bsq) ? {x: x, y: Math.sqrt(bsq - xsq)} : null;
        };

        me.redraw = function() {
          var canvas = $('#triangle')[0];
          var ctx = canvas.getContext('2d');
          ctx.fillStyle = '#ce827a';    // a warm skin tone
          ctx.strokestyle = '#863128'; 
          ctx.lineWidth = 1;

          var scale = Math.min(canvas.width, canvas.height) / 100.0;
          var a = me.a * scale,
              b = me.b * scale,
              c = me.c * scale;

          var x0 = 10, y0 = 10;
          var x1 = x0 + a, y1 = y0;
          ctx.clearRect(0, 0, canvas.width, canvas.height);

          ctx.beginPath();
          ctx.moveTo(x0, y0);
          ctx.lineTo(x1, y1);

          var corner = me.triangle_corner_coordinates(a, b, c);
          if (corner) {
            var x2 = x0 + corner.x, y2 = y0 + corner.y;
            ctx.lineTo(x2, y2);
            ctx.closePath();

            var display = function(n) { return parseFloat((n / scale).toFixed(8)); };
            $('.coords').text('('+display(corner.x)+', '+display(corner.y)+')');

            var dx2sq = corner.x * corner.x;
            var dy2sq = corner.y * corner.y;
            $('.calcb').text(display(Math.sqrt(dx2sq + dy2sq)));
            $('.calcc').text(display(Math.sqrt(Math.pow(x2 - x1, 2) + dy2sq)));
          }
          ctx.stroke();
          ctx.fill();
        };

        $(function() {
          $('.slider_with_display').slider_with_display({ step: 0.1 });
          $('#a').bind('slider_with_displaychange', function(event, ui) { me.a = ui.value; me.redraw(); });
          $('#b').bind('slider_with_displaychange', function(event, ui) { me.b = ui.value; me.redraw(); });
          $('#c').bind('slider_with_displaychange', function(event, ui) { me.c = ui.value; me.redraw(); });
          $('.slider_with_display').slider_with_display('value', 30);
        });
      </script>
    </body></html>

(End of `triangle.html`.)

This software is available via

    git clone http://canonical.org/~kragen/sw/inexorable-misc.git

(or in <http://canonical.org/~kragen/sw/inexorable-misc>) in the file
`triangle.html`.

Like everything else posted to kragen-hacks without a notice to the
contrary, this software is in the public domain.