```# By K. Urner, 4D Solutions

import povray
import coords
import functions
import primes
import series
import rand
import time
import turtles

def mkpacking(n):
# main (open, write, close)
myfile = povray.Povray("tripack4.pov")
myfile.sphcolor = "Green"
pascalpack(n,myfile)
# functions.xyzaxes(myfile,1.5)
myfile.close()

def tripack(n,myfile):
# output a triangular sphere packing
# as a povray file with n rows
vertdrop = (3**0.5)/2.0 * diameter
y_int = coords.Vector((       0,vertdrop,0))
x_int = coords.Vector((diameter,       0,0))
v1 = y_int * (n-1) # start at top, work down
for i in range(1,n+1):
v2 = v1
for j in range(i):
myfile.point(v2)
# shift right by sphere diameter
v2 = v2 + x_int
# end of row: drop down, shift left
v1 = v1 - y_int - coords.Vector((diameter/2.0,0,0))

def geoboard(n,myfile):
# output a triangular sphere packing
# as a povray file with n rows
vertdrop = (3**0.5)/2.0 * diameter
y_int = coords.Vector((       0,vertdrop,0))
x_int = coords.Vector((diameter,       0,0))
# start at top, work down
v1 = y_int * (n-1) * 0.5
for i in range(1,n+1):
v2 = v1
for j in range(i):
myfile.point(v2)
# shift right by sphere diameter
v2 = v2 + x_int
# end of row: drop down, shift left
v1 = v1 - y_int - coords.Vector((diameter/2.0,0,0))

def randtri(n,myfile):
# draw descending randomized left/right edges
# in a tripacking
vertdrop = (3**0.5)/2.0 * diameter
vectorset = {}
v1 = coords.Vector(( diameter/2.0,-vertdrop,0.0))
v2 = coords.Vector((-diameter/2.0,-vertdrop,0.0))
oturtle = turtles.Turtle([v1,v2],myfile)
oturtle.color = "Orange"
# start at top, work down
oturtle.penup()   # stop tracing path
oturtle.goto(coords.Vector((0,vertdrop,0)) * n * 0.5)
oturtle.pendown() # start tracing path
print vertdrop
print v1.coords
for i in range(n):
oturtle.randomwalk(1)

def pascalpack(n,myfile):
# output a triangular sphere packing
# as a povray file with 0,1,2...n rows
starttime = time.time()
global entries
entries = {}
vertdrop = (3**0.5)/2.0 * diameter
y_int = coords.Vector((       0,vertdrop,0))
x_int = coords.Vector((diameter,       0,0))
v1 = y_int * (n-1) * 0.5 # start at top, work down
for i in range(1,n+2):
print i-1 # display row/level
v2 = v1
for j in range(i):
rtnval = testpass4(i-1,j,n)
if   rtnval == 1: myfile.sphcolor = "Orange"
elif rtnval == 2: myfile.sphcolor = "Blue"
elif rtnval == 3: myfile.sphcolor = "Green"
elif rtnval == 4: myfile.sphcolor = "Red"
else: myfile.sphcolor = "Black"
myfile.point(v2)
# shift right by sphere diameter
v2 = v2 + x_int
# end of row: drop down, shift left
v1 = v1 - y_int - coords.Vector((diameter/2.0,0,0))
endtime = time.time()
print "Total seconds elapsed: ", endtime-starttime

def testpass1(i,j,n):
entry = series.pascal(i,j)
return primes.iseven(entry)

def testpass2(i,j,n):
entry = series.pascal(i,j)
return primes.isprime(entry)

def testpass3(i,j,n):
# Pascal's Triangle -- color coded for near-primes
if primes.isprime(entry):
rtnval = 1
else:
neg1  = primes.isprime(entry-1)
pos1  = primes.isprime(entry+1)
if neg1:  rtnval = 2
if pos1:  rtnval = 3
if neg1 and pos1: rtnval = 4  # overrides previous values
return rtnval

def testpass4(r,c,n):
# Pascal's Tetrahedron -- color coded for near-primes
rtnval=0
ijk = series.getijk(n,r,c)
ijk.sort()
index = str(ijk)
if entries.has_key(index):
return entries[index]   # return from cache if seen before

entry = series.pasctet(n,r,c)

if primes.isprime(entry):
rtnval = 1
else:
neg1  = primes.isprime(entry-1)
pos1  = primes.isprime(entry+1)

if neg1:  rtnval = 2
if pos1:  rtnval = 3
if neg1 and pos1: rtnval = 4  # overrides previous values

entries[index] = rtnval           # cache results
return rtnval
```