#!/usr/local/bin/perl                                -*- perl -*-
#

# There are four kinds of patches: NW, NE, SW, and SE.  They are named
# depending on the direction that the black triangle is pointing.
@patchdirs = ('nw', 'ne', 'se', 'sw');

# Each 2-block has four patches and is represented as a list of four patches.
# The nw patch first, then ne, se, and sw.

sub reflect_block {
    local($nw, $ne, $se, $sw) = @_;

    return (&reflect_patch($ne), &reflect_patch($nw),
	    &reflect_patch($sw), &reflect_patch($se));
}

sub reflect_patch {
    local($patch) = @_;

    $patch =~ tr/we/ew/;

    return $patch;
}

sub reverse_block_ground {
    local(@patches) = @_;
    local(@reversed) = ();

    for $p (@patches) {
	@reversed = (@reversed, &reverse_patch_ground($p));
    }
   
    return @reversed;
}

sub rotate_block {
    local($nw, $ne, $sw, $se, $n) = @_;
    local(@patch) = ($nw, $ne, $sw, $se);

    $n = $n % 4;

    for $i (0 .. 3) {
	$patch[$i] = &rotate_patch($patch[$i], $n);
    }
    
    for $i (1 .. $n) {
	local($nw) = shift(@patch);
	@patch = (@patch, $nw);
    }

    return @patch;
}

sub rotate_patch_cw {
    return &rotate_patch(@_, 1);
}   
 
sub rotate_patch_ccw {
    return &rotate_patch(@_, 3);
}    

sub reverse_patch_ground {
    return &rotate_patch(@_, 2);
}    

# Rotate patch n*90 degrees.
sub rotate_patch {
    local($patch, $n) = @_;

    for $i (0 .. 3) {
	if ($patchdirs[$i] eq $patch) {
	    return $patchdirs[($i + $n)%4];
	}
    }

    print STDERR "Failed:  Couldn't recognize patch $patch.\n";
    return $patch;
}

sub compare_blocks_without_transformation {
    local($nw1, $ne1, $se1, $sw1, $nw2, $ne2, $se2, $sw2) = @_;

    return ($nw1 eq $nw2 
	    && $ne1 eq $ne2 
	    && $se1 eq $se2 
	    && $sw1 eq $sw2);

}

sub compare_blocks_with_transformation {
    local($nw1, $ne1, $se1, $sw1, $nw2, $ne2, $se2, $sw2) = @_;
    local(@b1) = ($nw1, $ne1, $se1, $sw1);
    local(@b2) = ($nw2, $ne2, $se2, $sw2);
    local(@tb);

    for $rot (0 .. 3) {
	for $inv (0 .. 1) {
	    for $ref (0 .. 1) {
		@tb = &rotate_block(@b1, $rot);
		@tb = &reverse_block_ground(@tb) if ($inv == 1);
		@tb = &reflect_block(@tb) if ($ref == 1);

		return 1 if &compare_blocks_without_transformation(@tb, @b2);
	    }
	}
    }

    return 0;
}    

sub enumerate_all_blocks {
    local(@the_block) = ('nw', 'nw', 'nw', 'nw');
    local($pos) = 0;

    local(@all_blocks) = @the_block;

    while (1) {
	while ($the_block[$pos] eq 'sw') {
	    $the_block[$pos++] = 'nw';
	}
	if ($pos gt 3) { return @all_blocks; }
	$the_block[$pos] =  &rotate_patch_cw($the_block[$pos]);
	$pos = 0;
	@all_blocks = (@all_blocks, @the_block);
    }

}

@all_blocks = &enumerate_all_blocks();
$nb = ($#all_blocks + 1) / 4;
print "Constructed list of $nb potential blocks.\n";

@unique_blocks = ();

BLOCK:
    while (@all_blocks) {
	$count++;

	$nw = shift(@all_blocks);    
	$ne = shift(@all_blocks);    
	$se = shift(@all_blocks);    
	$sw = shift(@all_blocks);
	@b = ($nw, $ne, $se, $sw);
	
	print "Now examining block #$count (@b).\n";

	@ubls = @unique_blocks;
	$is_new_block = 1;

      UNIQUE_BLOCK:
	while (@ubls) {
	    $nw = shift(@ubls);    
	    $ne = shift(@ubls);    
	    $se = shift(@ubls);    
	    $sw = shift(@ubls);
	    @ub = ($nw, $ne, $se, $sw);
	    
	    if (&compare_blocks_with_transformation(@b, @ub)) {
		print "Blocks (@b) and (@ub) are the same.\n";
		next BLOCK;
	    }
	}
	# If we got here, this block is new.
	@unique_blocks = (@unique_blocks, @b);
	$num_unique_blocks++;
	print "$num_unique_blocks unique out of $count examined so far.\n";
    }


$count = 1;
while (@unique_blocks) {
    $nw = shift(@unique_blocks);    
    $ne = shift(@unique_blocks);    
    $se = shift(@unique_blocks);    
    $sw = shift(@unique_blocks);
    @b = ($nw, $ne, $se, $sw);

    print "$count: @b\n";
    $count++;
}
