sample.pl


#!/usr/bin/perl -wT
#
# Reference implementation for the geohashing algorithm described in xkcd #426
# See http://wiki.xkcd.com/ProjectRainbow/Main_Page
#
# Dan Boger (zigdon@gmail.com)
# 2008-05-21

use strict;
use Digest::MD5 qw/md5_hex/;
use Date::Manip;
use Getopt::Std;

my %opts;
getopts('e', \%opts);

# get the date from the commandline, or assume it's today's date
my $date = shift || "";
$date ||= 'today';
my $datestring = UnixDate( ParseDate($date), "%Y-%m-%d" );

die "Usage: $0 [-e] <date> [DJI opening]"
  unless $datestring and $datestring =~ /^\d\d\d\d-\d\d-\d\d$/;

# get the DJIA from the commandline, or try to retrieve it from google
my $djia = shift;
unless ($djia) {
    $djia = &download_djia($date, $opts{e});
}

print "Date: $datestring, DJIA: $djia\n";

# calculate the MD5 of the combined date and DJIA
my $md5 = md5_hex("$datestring-$djia");
print "MD5($datestring-$djia): $md5\n";

# split into two
my ( $md5x, $md5y ) = ( substr( $md5, 0, 16 ), substr( $md5, 16, 16 ) );
print "Split: $md5x, $md5y\n";

# transform into a fraction
my ( $fx, $fy ) = ( 0, 0 );
while ( length $md5x or length $md5y ) {
    my $d = substr( $md5x, -1, 1, "" );
    $fx += hex $d;
    $fx /= 16;

    $d = substr( $md5y, -1, 1, "" );
    $fy += hex $d;
    $fy /= 16;
}
printf "Fractions: %0.16f, %0.16f\n", $fx, $fy;

sub download_djia {
    my $date = shift;
    my $use_30w_rule = shift;

    my $datestring;
    if ($use_30w_rule) {
       print "Adjusting for 30W\n";
       $date = DateCalc(ParseDate($datestring), "-1 day");
       $datestring = UnixDate( ParseDate($date), "%Y-%m-%d" );
    } else {
       $datestring = UnixDate( ParseDate($date), "%Y-%m-%d" );
    }

    my $djia;

    require LWP::Simple;
    import LWP::Simple 'get';
    require HTML::TreeBuilder;

    my $URL =
'http://finance.google.com/finance/historical?cid=983582&startdate=%s&enddate=%s';
    $URL = sprintf( $URL,
        UnixDate( DateCalc( $date, "- 7 days" ), "%b+%d,+%Y" ),
        UnixDate( $date, "%b+%d,+%Y" ) );
    print "Downloading DJIA from google: $URL\n";
    my $page = get($URL);
    die "Failed to get DJIA from google!" unless $page;
    my $tree = HTML::TreeBuilder->new_from_content($page)
      or die "Failed to parse google DJIA page!";

    my $data = $tree->look_down( id => "prices" ) or die "No data div: $!";
    foreach my $td ( $data->look_down( class => "firstcol" ) ) {
        next if $td->as_text eq 'Date';
        $djia = $td->right->as_text;
        $djia =~ s/,//g;
        print "DJIA opening for $datestring is $djia\n";
        last;
    }

    $tree->delete;

    die "Failed to retrieve DJIA from google!" unless $djia;

    return $djia;
}