There are many ways to optimize this program, however I'm going to avoid these optimizations and try to create a naive implementation of the problem, just to see to the program looks like in Fortress. Also I'm going to try to take advantage of the parallel for loops that Fortress provides.

The first thing to define is something to represent complex numbers (remember this is a non-optimize version of the problem) . It seems that in the current reference implementation of there's no native support complex number (that I could find). So this gave me the opportunity to implement it.

value object Complex( real:RR64, img:RR64)

opr+(self,other:Complex) = Complex(real+other.real,img+other.img)

opr juxtaposition(self,other:Complex) =

Complex((real other.real) - (img other.img),

(real other.img) + (other.real img))

toString() = ("Complex(" real ", " img ")")

end

opr |x:Complex| = SQRT(x.real^2 + x.img^2)

complexExp (o : Complex,n : Integral) = do

if (n = 1)

then o

else (o complexExp(o,n - 1))

end

end

opr^(o:Complex,n:Integral) =

if (n = 2)

then (o o)

else complexExp(o,n)

end

Note that this implementation is not complete, only the required elements for the Mandelbrot set are implemented. It is interesting that multiplication in Fortress doesn't use the '*' operation but it uses juxtaposition of elements to be multiplied. That is, instead of saying (x*y) you say (x y).

Then we need something that helps us to convert from screen coordinates to real coordinates. To do this I created the following function:

lFunc(x1 : RR64,y1 : RR64, x2 : RR64, y2 :RR64) = do

m = (y2 - y1) / (x2 - x1)

b = y1 - (m x1)

fn(x) => (m x) + b

end

Note that this function returns another function that preforms the conversion.

Now the following code shows the implementation of the Mandelbrot set algorithm for one row in the image.

lineMandelbrot[\W\](startIndex : ZZ32, endIndex : ZZ32,

lineData :Array[\ZZ32,ZZ32\],

f :RR64 -> RR64, y : RR64) = do

for i <- startIndex:endIndex do

p0 = Complex( f(i) , y)

p : Complex := p0

j : ZZ32 := 0

maxIteration = 255

while ( |p| < 2.0 AND j < maxIteration ) do

p := p^2 + p0

j := j+1

end

lineData[i] := j

end

end

Note that I use a parallel for loop for the external for statement in order to say that each point can be calculated independently in a separate thread.

This code looks very nice when formated with Fortify:

Finally the following code show the main program. For the graphic generation, a PPM file was used since is the easiest image format to generate!.

run(a:String...) =

do

imageWidth = 100

imageHeight = 100

startY = -1.0

endY = 1.0

startX = -1.0

endX = 1.0

image = array[\ZZ32\](imageWidth)

fout: BufferedWriter = outFileOpen "output.ppm"

outFileWrite(fout,"P3\n")

outFileWrite(fout,"" imageWidth " " imageHeight "\n")

outFileWrite(fout,"255\n")

startIY = 1

endIY = imageHeight

fY = lFunc(startIY,startY,endIY,endY)

fX = lFunc(0,startX,imageWidth - 1,endX)

for iY <- seq(startIY#endIY) do

print "Line: " iY "\n"

lineMandelbrot(0,imageWidth - 1,image,fX,fY(iY))

for i <- seq(0#imageWidth) do

outFileWrite (fout,"0 0 " image[i] " ")

end

outFileWrite(fout,"\n")

end

outFileClose(fout)

()

end

Since the reference implementation focus is not performance, I think is not fair to publish benchmarks or something like that. However I noticed that by playing around with the

`NumFortressThreads`

environment variable (found by looking at the source code) I got different execution times in my dual core machine .
## No comments:

Post a Comment