#!/usr/bin/env perl
# * cperl
BEGIN { $W=1}
use strict;
use diagnostics;
use warnings;
use Carp;
use English;
use Math::Trig;
use GD;
use constant P| => 4*atan(1);
# June 30, 2007
# Allan Peda
#
# Ulam prime number spiral
# http://mathworld.wolfram.conyPrimeSpiral.html
# http://en.wikipedia.org/wiki/Ulam_ spiral
#
# we iterate over a grid, but we want to follow a spiral
# Direction is cartesian based x being horizontal motion,
# and y being vertical. Up and right are postive motion.
unless (defined ($ARGV[0])){
die "Please provide a png file to save to.";
}
my $pnogfile = ‘ulam_spiral.pno'
if($ARGV[O] =~ nv\.pnaS/i){
$pngfile = $ARGV[0];
} else {
print "Supplied file not suffixed with .png defaulting to \"$pngfile\".\n";
}
# how many turns to make in the spiral
my $num_corners = 1600;
my @turning_points = mkturns($num_corners) ;
# which directions do we follow
my $max = $turning_points [$#turning_points ];
print "Generating map for first $max primes.\n";
my ($x width, $y width) = (0,0);
$xwidth = $y width = int(sqrt($max+.5));print "Dimensions are: $x width x $y_width\n";
my %primes = %{genprimes($max)};
my $im new GD: :Image($x_width, $y_width) ;
my $white $im->colorAllocate (255,255,255) i
my $black $im->colorAllocate(0,0,0) ;
my $origin_color = $im->colorAllocate(255,0,0) ;
my $position=$turning points [0] ;
my ($x, $y) = (0,0);
my (Sdelx,$dely) = (1,0); # start out moving to the right
sub gdx { return( shift()+($x_width/2) ) }
sub gdy { return( ($y_width/2)-shift()) }
$im->setPixel (gdx($x) , gdy($y) , $origin_color) ;
foreach my $next_turn (@turning points) {
for (my Si=$position;$i<$next_turn;++$i) {
# move to next cartesian
$x t= $delx;
$y 4= $dely;
# print "x=$x, y=$y (i = $i)\n";
if ($i==$next_tum) {
($delx, $dely) = nextDirection($delx,$dely) ;
if ( defined ($primes{$it1})) { # origin starts at n=1, not n=0
# translation to gd coordinate system
$im->setPixel (gdx ($x) ,gdy($y) ,$black) ;
+
$position = $next_tum+1;
open (PNGFH, '>".$pngfile)
or die "Could not open image file \"$pngfile
print PNGFH $im->png;
close PNGFH;
# an array of run lengths up to each turn
# equation was guessed at after inspection of coordinates
# froma hand drawn cartesian spiral
sub mkturns{
my $ulim
my $len
hift;my @rv;
foreach my $i (1..$ulim) {
my $delta = ($i%2?$i+1:$1)/2;
$len = $len+$delta;
push( @rv, $len);
return @rv;
# generates prime numbers as hash keys
sub genprimes {
my $max =
my $sieve
my %primes = (2 => 1);
GUESS: for (my $guess = 2; $guess <= $max; $guesstt) {
next GUESS if vec($sieve,$guess,1) ;
$primes{$guess} = 1;
for (my $mults = $guess * $guess; $mults <= $max; $mults += $guess) -
vec ($sieve, $mults,1) = 1;
hift;
}
return \%primes;
# uses memoized trig function
# function modded to go past 180 degrees
{
my %memo = ();
sub nextDirection {
my ($x, $y) = (shift, shift);
my $nextoffset = undef;
if( defined ($memo{$x}{$y})){
$nextoffset = $memo{$x}{$y};
} else {
my $acos = acos($x);
my $asin = asin($y);
if( $asin < O){
$asin+=(PI*2) ;
+
my $offset = int(2*($acos>$asin?$acos:$asin) /PI) ;
# hard coded count of four directions in spiral (right, up, left down)
$nextoffset = $memo{$x}{$y} = (Soffset+1)%4;
}
return to_xy($nextoffset) ;