#!/usr/bin/perl
my $level = shift;
my $dist = shift || 2 ** $level;

my $filename = shift;

if ($filename)
{
    open SVG, '>', $filename;
}
else
{
    *SVG = *STDOUT;
}

print SVG qq|<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg viewBox="0 0 $dist $dist" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
  <title>Hilbert curve of level $level</title>
  <defs>
    <path id="hilbert" d="|;

$dist /= 2 ** $level;
goToXY($dist/2, $dist/2);
#goToXY(0, 0);
HilbertA($level);

print SVG qq|"/>
  </defs>
  <use xlink:href="#hilbert" fill="none" stroke="blue" stroke-width="1"/>
  <text>
    <textPath xlink:href="#hilbert">
<![CDATA[
In a hotel with a finite number of rooms, it is clear that once it is full, no more guests can be accommodated. Now, imagine a hotel with an infinite number of rooms. One might assume that the same problem will arise when all the rooms are occupied. However, in an infinite hotel, the situations "every room is occupied" and "no more guests can be accommodated" do not turn out to be equivalent. There is a way to solve the problem: if you move the guest occupying room 1 to room 2, the guest occupying room 2 to room 3, etc., you can fit the newcomer into room 1. Unlike a finite hotel, in an infinite hotel, being "full" in the sense that every room contains a person is not the same as being "full" in the sense that there is no space for another person. Note that a movement of an infinite number of guests would constitute a supertask.

It is also possible to make room for a countably infinite number of new clients: just move the person occupying room 1 to room 2, occupying room 2 to room 4, occupying room 3 to room 6, etc., and all the odd-numbered new rooms will be free for the new guests.

Now imagine a countably infinite number of coaches arrive, each with a countably infinite number of passengers. Still, the hotel can accommodate them: first empty the odd numbered rooms as above, then put the first coach's load in rooms 3n for n = 1, 2, 3, ..., the second coach's load in rooms 5n for n = 1, 2, ... and so on; for coach number i we use the rooms pn where p is the i+1-st prime number. You can also solve the problem by looking at the license plate numbers on the coaches and the seat numbers for the passengers (if the seats are not numbered, number them). Regard the hotel as coach #0. Interleave the digits of the coach numbers and the seat numbers to get the room numbers for the guests. The guest in room number 1729 moves to room 1070209. The passenger on seat 8234 of coach 56719 goes to room 5068721394 of the hotel.

Some find this state of affairs profoundly counterintuitive. The properties of infinite 'collections of things' are quite different from those of ordinary 'collections of things'. In an ordinary hotel, the number of odd-numbered rooms is obviously smaller than the total number of rooms. However, in Hilbert's aptly named Grand Hotel the 'number' of odd-numbered rooms is as 'large' as the total 'number' of rooms. In mathematical terms, this would be expressed as follows: the cardinality of the subset containing the odd-numbered rooms is the same as the cardinality of the set of all rooms. In fact, infinite sets are characterized as sets that have proper subsets of the same cardinality. For countable sets this cardinality is called ℵ₀ (aleph-null).

An even stranger story regarding this hotel shows that mathematical induction only works in one direction. No cigars may be brought into the hotel. Yet each of the guests (all rooms had guests at the time) got a cigar while in the hotel. How is this? The guest in Room 1 got a cigar from the guest in Room 2. The guest in Room 2 had previously received two cigars from the guest in Room 3. The guest in Room 3 had previously received three cigars from the guest in Room 4, etc. Each guest kept one cigar and passed the remainder to the guest in the next-lower-numbered room.
]]>
    </textPath>
  </text>
</svg>|;

sub HilbertA
{
    my $level = shift;
    if ($level > 0)
    {
        HilbertB($level-1);    lineRel(0, $dist);
        HilbertA($level-1);    lineRel($dist, 0);
        HilbertA($level-1);    lineRel(0, -$dist);
        HilbertC($level-1);
    }
}

sub HilbertB
{
    my $level = shift;
    if ($level > 0)
    {
        HilbertA($level-1);    lineRel($dist, 0);
        HilbertB($level-1);    lineRel(0, $dist);
        HilbertB($level-1);    lineRel(-$dist, 0);
        HilbertD($level-1);
    }
}

sub HilbertC
{
    my $level = shift;
    if ($level > 0)
    {
        HilbertD($level-1);    lineRel(-$dist, 0);
        HilbertC($level-1);    lineRel(0, -$dist);
        HilbertC($level-1);    lineRel($dist, 0);
        HilbertA($level-1);
    }
}

sub HilbertD
{
    my $level = shift;
    if ($level > 0)
    {
        HilbertC($level-1);    lineRel(0, -$dist);
        HilbertD($level-1);    lineRel(-$dist, 0);
        HilbertD($level-1);    lineRel(0, $dist);
        HilbertB($level-1);
    }
}

sub goToXY
{
    my ($x, $y) = @_;
    print SVG "M $x $y ";
}

sub lineRel
{
    my ($dX, $dY) = @_;

    if ($dY == 0)
    {
        print SVG "h $dX ";
    }
    elsif ($dX == 0)
    {
        print SVG "v $dY ";
    }
    else
    {
        print SVG "l $dX $dY ";
    }
}
