Hi all,

As the GLBasic math functions do not support imaginary or complex numbers, I created TYPE CMPLX and a basic set of function commands for their use.

You may want to create a small font for this program called "myfont.png", which is large enough to read. Otherwise the default mouse-font is hard on the eyes.

There is definitely room for spit and polish, and if anyone wants to offer or make improvements, you won't offend me.

The function, COMPLEX2STR$, for example, formats the complex number to a BYREF-passed string, because functions can only return a single value. Using it for printed output is a bit of a dance, but it was the only way I could figure to get the job done. Additionally, the string which the function creates has an abundance of parentheses from an abundance of caution on my part towards respecting the mathematical order of operations. There is room for simplicity, but you can enter the displayed equations exactly as shown into a TI-84 calculator and verify that the functions are calculating properly.

As a demonstration and for a bit of fun, I finished this program by using complex numbers to calculate a Mandelbrot Set.

The program is structured so that you can simply omit the function-demonstration and the Mandelbrot sub and use the Complex Type and Functions for your own programs.

Anyway, I hope you have as much fun messing with this program as I had writing it.

Cheers!

-CW

// --------------------------------- //

// Project: Complex_Numbers V_1.0

// Start: Sunday, March 03, 2013

// IDE Version: 10.283

//Please create a small font with text large enough for you to read and save it as "myfont.png".

//////////////////////////////////////////////////////////

// Complex Number Command Set //

//------------------------------------------------------//

// Written by Craig Waterman. 3/03/13 //

///////////////////////////////////////////////////////////////////////

// //

// Note to Reader //

// -------------- //

// The body of this program contains a demonstration using each //

// Complex Number function for variables defined as TYPE COMPLEX. //

// Following the body are the functions themselves. //

// Following the functions is a modual which demostrates //

// how to generate a Manderbal Set on the complex plane. //

// The only way to create a Manderbel set is using complex numbers. //

// Both the BODY secion and the Manderbel modual can be //

// deleted cleanly, leaving just the Complex functions. //

// Enjoy! -CW //

///////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////

/////// BODY /////////

//////////////////////////////////////////////////////

GLOBAL SCREENWIDTH = 1000,SCREENHEIGHT = 510

SETSCREEN SCREENWIDTH,SCREENHEIGHT,0

LOADFONT "myfont.png",0

SETFONT 0

TYPE CMPLX

real# = 0.0

imaginary# = 0.0

ENDTYPE

LOCAL CMPLX_Answer AS CMPLX

LOCAL Var1 AS CMPLX

LOCAL Var2 AS CMPLX

LOCAL Var3 AS CMPLX

LOCAL HoldAnswer AS CMPLX

LOCAL MyString$

Var1.real = 4.0; Var1.imaginary = 2.0

Var2.real = 4.0; Var2.imaginary = -3.0

ADDCOMPLEX(Var1,Var2,HoldAnswer)

MyString$=""

COMPLEX2STR$(Var1,MyString$,TRUE);MyString$=MyString$+" + "

COMPLEX2STR$(Var2,MyString$,TRUE);MyString$=MyString$+" = "

COMPLEX2STR$(HoldAnswer,MyString$,FALSE)

PRINT MyString$,30,10

Var3 = HoldAnswer

SUBTRACTCOMPLEX(Var3,Var1,HoldAnswer)

MyString$=""

COMPLEX2STR$(Var3,MyString$,TRUE);MyString$=MyString$+" - "

COMPLEX2STR$(Var1,MyString$,TRUE);MyString$=MyString$+" = "

COMPLEX2STR$(HoldAnswer,MyString$,FALSE)

PRINT MyString$,30,30

MULTIPLYCOMPLEX(Var1,Var2,HoldAnswer) // Var1 * Var2 = HoldAnswer. (HoldAnswer holds the return answer.)

Var3 = HoldAnswer // Hang on to the mustiplication answer so we can use it for division later.

MyString$=""

COMPLEX2STR$(Var1,MyString$,TRUE);MyString$ = MyString$+" X "

COMPLEX2STR$(Var2,MyString$,TRUE);MyString$ = MyString$+" = "

COMPLEX2STR$(HoldAnswer,MyString$,FALSE)

PRINT MyString$,30,50

DIVIDECOMPLEX(Var3,Var2,HoldAnswer)

MyString$=""

COMPLEX2STR$(Var3,MyString$,TRUE);MyString$=MyString$+" / "

COMPLEX2STR$(Var2,MyString$,TRUE);MyString$=MyString$+" = "

COMPLEX2STR$(HoldAnswer,MyString$,FALSE)

PRINT MyString$,30,70

MyString$="";COMPLEX2STR$(Var1,MyString$,FALSE)

SQUARECOMPLEX(Var2,HoldAnswer)

MyString$="";

COMPLEX2STR$(Var2,MyString$,TRUE);MyString$ = MyString$+"^2 ="

COMPLEX2STR$(HoldAnswer,MyString$,FALSE)

PRINT MyString$,30,90

RECIPCOMPLEX(Var1,HoldAnswer)

MyString$=""

COMPLEX2STR$(Var1,MyString$,TRUE);MyString$=MyString$+"^-1 = "

COMPLEX2STR$(HoldAnswer,MyString$,FALSE)

PRINT MyString$,30,110

PRINT "Press KEY to continue.",150,150

SHOWSCREEN;KEYWAIT

PRINT "Let's use complex numbers to calculate a Mandelbrot Set.",20,10

PRINT "Press KEY to begin.",250,50

SHOWSCREEN;KEYWAIT

GOSUB CREATE_MANDELBROT

SHOWSCREEN;KEYWAIT

END

///////////////////////////////////////////////////////////////////

////// END BODY //////

///////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////

// Complex Number Functions. //

// ------------------------------------------------------------- //

// --- MULTIPLYCMPX --- //

// ------------------------------------------------------------- //

FUNCTION MULTIPLYCOMPLEX: Num1 AS CMPLX, Num2 AS CMPLX,Answer AS CMPLX

LOCAL Temp AS CMPLX // Temp is used so we won't have to change the value

// of the variable 'Answer' points to until the very end.

// This allows the user to call these functions with

// one variable and request that the answer be returned

// in the vary same variable. Without the Temp variable

// we would run into trouble overwriting values we still need.

Temp.real = (Num1.real * Num2.real) - (Num1.imaginary * Num2.imaginary)

Temp.imaginary = (Num1.real * Num2.imaginary) + (Num2.real * Num1.imaginary)

Answer = Temp

ENDFUNCTION // MULTIPLYCMPX

// ------------------------------------------------------------- //

// --- ADDCOMPLEX ---

// ------------------------------------------------------------- //

FUNCTION ADDCOMPLEX: n1 AS CMPLX, n2 AS CMPLX, Answer AS CMPLX

LOCAL Temp AS CMPLX

Temp.real = n1.real + n2.real

Temp.imaginary = n1.imaginary + n2.imaginary

Answer = Temp

ENDFUNCTION // ADDCOMPLEX

// ------------------------------------------------------------- //

// --- SQRCOMPLX ---

// ------------------------------------------------------------- //

FUNCTION SQUARECOMPLEX: CMPLX_Num AS CMPLX,Answer AS CMPLX

LOCAL Temp AS CMPLX

MULTIPLYCOMPLEX(CMPLX_Num,CMPLX_Num,Temp)

Answer = Temp

ENDFUNCTION // SQRCOMPLX

// ------------------------------------------------------------- //

// --- DIVIDECOMLEX ---

// ------------------------------------------------------------- //

FUNCTION DIVIDECOMPLEX: CMPLX_v1 AS CMPLX, CMPLX_v2 AS CMPLX, Answer AS CMPLX

LOCAL Inverse AS CMPLX,Temp AS CMPLX

RECIPCOMPLEX(CMPLX_v2,Inverse)

MULTIPLYCOMPLEX(CMPLX_v1,Inverse,Temp)

Answer = Temp

ENDFUNCTION // DIVIDECOMLEX

// ------------------------------------------------------------- //

// --- SUBTRACTCOMPLEX ---

// ------------------------------------------------------------- //

FUNCTION SUBTRACTCOMPLEX: n1 AS CMPLX, n2 AS CMPLX, Answer AS CMPLX

LOCAL Num AS CMPLX,Temp AS CMPLX

Num = n2

Num.real = -1*Num.real

Num.imaginary = -1*Num.imaginary

ADDCOMPLEX(n1,Num,Temp)

Answer=Temp

ENDFUNCTION // SUBTRACTCOMPLEX

// ------------------------------------------------------------- //

// --- RECIPCOMPLEX --- Finds num^-1

// ------------------------------------------------------------- //

FUNCTION RECIPCOMPLEX: n1 AS CMPLX,Answer AS CMPLX

LOCAL Temp AS CMPLX

LOCAL LCD# = 0.0

LCD = POW(n1.real,2)+ POW(n1.imaginary,2)

Temp.real = (n1.real/LCD)

Temp.imaginary = (n1.imaginary / LCD *-1)

Answer=Temp

ENDFUNCTION // RECIPCOMPLEX

// ------------------------------------------------------------- //

// --- COMPLEX2STR$ --- Note: Add_Parentheses = TRUE means place parentheses around the entire complex number.

// ------------------------------------------------------------- //

FUNCTION COMPLEX2STR$: CplxNum AS CMPLX, BYREF String$,Add_Parentheses%

LOCAL WorkStr$ = "",Hold$=""

IF Add_Parentheses = TRUE THEN String$=String$+"("

String$=String$+"("

WorkStr$=CplxNum.real

FOR counter = 0 TO LEN(WorkStr$);Hold$=MID$(WorkStr$,counter,1);IF Hold$<>" " THEN String$=String$+Hold$;NEXT //The system formats numbers with blank spaces for polarity sign. Strip the spaces out.

IF INSTR(WorkStr$,".",0) = -1;String$=String$+".0)+(" ;ELSE;String$=String$+")+(";ENDIF // We want whole numbers, such as "4", to be displayed as with decimals, such as "4.0"

WorkStr$=CplxNum.imaginary;Hold$=""

FOR counter = 0 TO LEN(WorkStr$);Hold$=MID$(WorkStr$,counter,1);IF Hold$<>" " THEN String$=String$+Hold$;NEXT //The system formats numbers with blank spaces for polarity sign. Strip the spaces out.

IF INSTR(WorkStr$,".",0) = -1; String$=String$+".0i)";ELSE;String$=String$+"i)";ENDIF // Display whole numbers, such as "4" as "4.0"

IF Add_Parentheses = TRUE THEN String$=String$+")"

ENDFUNCTION // COMPLEX2STR$

//////////////////////////////////////////////////////////////////

//// END FUNCTIONS ////

//////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////

// ------------------------------------------------------------ //

// --- CREATE_MANDELBROT --- //

// Demenstration, Use Of Complex Number Functions. //

// ------------------------------------------------------------ //

// Written by Craig Waterman, Jan, 2013 //

//--------------------------------------------------------------//

// Safe to delete this entire sub and all functions below it. //

//////////////////////////////////////////////////////////////////

SUB CREATE_MANDELBROT:

// Mendalbrot Set Formula is Z=Z^2+C

LOCAL Z AS CMPLX

LOCAL C AS CMPLX

LOCAL Zero AS CMPLX // Zero has both components, real and imaginary, = 0. Used to clear CMPLX variable. (Ex: Z = Zero)

LOCAL MaxIterations#,Magnitude

LOCAL Cheat_For_Speed_Flag%

LOCAL PictureHeight_in_Pixels%,X_Pixels%,Y_Pixels%

LOCAL Fieldlength#,FieldShift#

LOCAL Mand_Start_X#,Mand_Start_Y#,Mand_End_X#,Mand_End_Y#,Mand_Rez_X#,Mand_Rez_Y#

LOCAL ColorPixelFlag%

////////////////////////////////////////

// User adjustable variables

//

PictureHeight_in_Pixels = 500 // Change these values to render a larger or smaller image. Keep it within your computers max screen size.

MaxIterations# = 1000 // You have to stop iterating sometime, and a maximum of 1000 iterations is sufficient for our resolution.

// Higher magnifications of the Mandelbrot set can require more iterations TO bring out the detail.

Magnitude = 3000 // This is an (arbitrary) cutoff point for how large any single number on the complex plane will be allowed to grow.

Cheat_For_Speed_Flag = FALSE // Set to TRUE for a fast render, but with lower accuracy.

/////////////////////////////////////////

// Here we set the range and domain of the section of Mandelbrot we will be looking at.

// These setting values were chosen to include the full Mandelbrot set.

// But just as any two points on a number line have an unlimited number of points between them,

// we can zoom into the Mandelbrot simply by choosing smaller interval between the start and end points,

// and then incrementing by smaller amounts.

//

// To build on this program, you could allow the user to drag a frame across a section of the

// Mandelbrot set on the screen, and obtain new start and end points from that, so to render

// a new view based on those values. This would allow the user to zoom into the Mandelbrot set.

// Because this is only a demonstration of the complex-number functions, I did not implement that feature.

IF PictureHeight_in_Pixels > SCREENWIDTH-10 THEN SCREENWIDTH = PictureHeight_in_Pixels+10

IF PictureHeight_in_Pixels > SCREENHEIGHT-10; SCREENHEIGHT = PictureHeight_in_Pixels+10

CLEARSCREEN;SHOWSCREEN

SETSCREEN SCREENWIDTH,SCREENHEIGHT,FALSE

ENDIF

X_Pixels = PictureHeight_in_Pixels-1;Y_Pixels = PictureHeight_in_Pixels-1

Fieldlength = 3.0/2;FieldShift = 0.65

Mand_Start_X# = 0-Fieldlength-FieldShift;Mand_End_X# = Fieldlength-FieldShift//+Mand_Start_X

Mand_Start_Y# = 0-Fieldlength ; Mand_End_Y# = Fieldlength

Mand_Rez_X# = (Mand_End_X - Mand_Start_X)/X_Pixels // This is the incriment value of each X pixel.

Mand_Rez_Y# = (Mand_End_Y - Mand_Start_Y)/Y_Pixels // This is the incriment value of each Y pixel.

FOR CountX=0 TO X_Pixels;FOR CountY=0 TO Y_Pixels

// The Mandelbrot formula couldn't be easier. It is Z=Z^2+C, where both Z and C are complex numbers.

// It is odd at first to think of one number having two components, but complex numbers do. Every point on the complex plain represents

// a single number. The two components of that number give a horizontal position and a vertical position, like a grid.

// One horizontal axis is the old real number line you know and love.

// The vertical axis is the "imaginary" number line, which is at a right angle to the real number line.

// (Imaginary numbers are every bit as 'real' as counting numbers, they just are not found on the real number line.)

// These two components form a sort of address on the complex plane, having a real component along the horizontal axist,

// and an imaginary component along the vertical. These "address" are plugged into Z=Z^2+C.

//

// Z always starts off equal to zero, while the complex number C holds the current address for the pixel we are calculating for.

// Then we take the square of Z, add C and plug that back into Z. This is the new value of Z. C doesn't change.

// We repeat this process again and again, for however many itterations we wish. Then we at the final value of Z.

//

// Some values on the complex plane don't go very far from where they started. Some take off very quickly towards infinity.

// The rest sort of take their time in growing larger, but grow they do, at leasurely pace.

// There is no reason to wait until a Z gets to infinity, or anywhere close, to figure out how that complex number is acting.

// All we have to do is watch how quickly it exceeds a set number, say 1000 (our Magnitude number), then base the color we will

// paint the pixel for the position on that. This is how each pixel gets its color.

Z=Zero // Clear the values out of Z.

C.real = Mand_Start_X+Mand_Rez_X*CountX;C.imaginary=Mand_Start_Y+Mand_Rez_Y*CountY //C = the current location-point on the complex number plane.

ColorPixel(CountX,CountY,0) //All pixels which fail to reach Magnitude by the time we run all of our itterations get colored black.

//For effency, we will start out setting the pixel black, run the itteration loop and change the pixel color if we need to.

FOR Iter = 0 TO MaxIterations

SQUARECOMPLEX(Z,Z) // Z=Z^2

ADDCOMPLEX(Z,C,Z) // Z=Z+C (so taken together theses two lines are Z=Z^2+C)

IF Cheat_For_Speed_Flag = TRUE

IF (ABS(Z.real) > Magnitude OR ABS(Z.imaginary) > Magnitude) THEN ColorPixelFlag = TRUE //Fast but causes frilled border artififact.

ELSE

IF SQR(POW(Z.real,2)+POW(Z.imaginary,2))>Magnitude THEN ColorPixelFlag = TRUE // Slow but accurate.

ENDIF

IF ColorPixelFlag = TRUE;ColorPixelFlag = FALSE;ColorPixel(CountX,CountY,Iter);BREAK;ENDIF //If yes, color pixel and abort loop.

//If no, continue iterations.

NEXT

// The 'cheat' If/Then line above, where I check magnitudes of the X and Y, does a decent job, but it

// produces the frilly-edge artifact on the red boundary which you will see.

// For greater accuracy, set the Cheat_For_Speed_Flag to FALSE. The red edge will be smooth, but rendering takes seven times longer.

// That may not matter if you have a beefy graphics card.

NEXT;USEASBMP;SHOWSCREEN;NEXT //Next y-pixel; Next x-pixel

PRINT "Complex Numbers are Fun!",550,150

ENDSUB // CREATE_MANDELBROT

// ------------------------------------------------------------- //

// --- ColorPixel --- //

// Colors current pixel the RGB color returned by Mandel_Color //

// ------------------------------------------------------------- //

FUNCTION ColorPixel:X_grid,Y_grid,CyclesExecuted // The pixle color is determined by how quickly a location on the Imaginary Plane exceeded the

// maximum allowable magnitude. (How quickly the address headed towards infinity.)

SETPIXEL 10+X_grid,5+Y_grid,Mandel_Color(CyclesExecuted)

ENDFUNCTION // ColorPixel

// ------------------------------------------------------------- //

// --- MANDEL_COLOR ---

// Assigns RGB color based on the number of itteation cycles executed to exceed Magnitude.

// ------------------------------------------------------------- //

FUNCTION Mandel_Color: Height

LOCAL a,b,c

a=0;b=0;c=0

SELECT Height

CASE 1 TO 5;a=0;b=255;c=0

CASE 6 TO 7;a=255;b=0;c=0

CASE 8 TO 11;a=0;b=0;c=255

CASE 12 TO 16;a=255;b=255;c=255

CASE >16;a=255;b=0;c=0

DEFAULT; a=0;b=0;c=0;

ENDSELECT

RETURN RGB(a,b,c)

ENDFUNCTION // MANDEL_COLOR