GLBasic forum
Codesnippets => Math => Topic started by: CW on 2013Mar05

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 mousefont 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 BYREFpassed 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 TI84 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 functiondemonstration 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! =D
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 complexnumber functions, I did not implement that feature.
IF PictureHeight_in_Pixels > SCREENWIDTH10 THEN SCREENWIDTH = PictureHeight_in_Pixels+10
IF PictureHeight_in_Pixels > SCREENHEIGHT10; SCREENHEIGHT = PictureHeight_in_Pixels+10
CLEARSCREEN;SHOWSCREEN
SETSCREEN SCREENWIDTH,SCREENHEIGHT,FALSE
ENDIF
X_Pixels = PictureHeight_in_Pixels1;Y_Pixels = PictureHeight_in_Pixels1
Fieldlength = 3.0/2;FieldShift = 0.65
Mand_Start_X# = 0FieldlengthFieldShift;Mand_End_X# = FieldlengthFieldShift//+Mand_Start_X
Mand_Start_Y# = 0Fieldlength ; 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 locationpoint 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 frillyedge 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 ypixel; Next xpixel
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

Im surprised to find no replies here CW. A good bit of field work that displays the efforts of a creative and intelligent coder. Keep it up!

Thanks Brick. I just posted it against the day when someone might need it. Handy stuff for the right application. Could use some polish, and more than a little spit. But if I programed perfection, there would be no room for improvement. May that day never come. :good:
CW

Indeed, and life would become boring.
I will tinker with your code and see what occurs. Im the type of guy that (sometimes) takes something apart, puts it all back together and finds a few screws left over...cough :)

LOL. Sounds good Brick.
I often code a chicken, a boot and a bowling ball. I don't mind the boots and bowling balls, but those dang chickens bother me; so I look forward to seeing what you come up with. (Consider that a challenge.) =D
Cheers!
CW

Ever heard of "keep your mouth shut not to show your stupidity?"
Or to be diplomatic: To speak is silver, to be quiet is gold.
We are many of this proverb :)
(Freely translated from swedish, can't be bothered to look it up)
To say it clear. We just don't understand much of it :)

Hi Moru.
LOL. We have your saying in the United States as well, and I can certainly relate to it. There are so many topics I feel ignorant about. This explains my deep dislike of triviatype games.
"Which member of The Beetles was born first, John Lennon, Paul McCartney, George Harrison, or Ringo Starr?"
Hmm.. Beats me. :zzz:
"List the following continents in descending order, according to size (total land area); Europe, Antarctica, Asia, North America, South America, Australia, Africa."
Umm.. Asia... Europe? :S
"What was the top YouTube music video in 2012?"
Ah.. common. Can't I have a science question instead??! :giveup:
And so it goes.
Imaginary numbers, on the other hand, are really neat, useful, and very mindtwisty. I won't bore you with the mechanics. Let me try to blow your mind instead.
Imagine an ant clinging to a horizontal thread. That thread is the real number line you grew up with, the numbers you know and love. The ant can walk to the right, (1,2,3,4...) and he can walk to the left (3,2,1,0,1,2). The ant can spend his entire life on that thread and can count himself very happy because every number he needs is on that thread. How about Pi? Yep. It's there, between 3.0 and 4.0. So is e. So is the square root of 19723391. One thread, containing every answer to every math question which can be asked. But not so fast...
There is a dirty little secret lurking in the mathematical operations we learned in grade school. Addition, Subtraction, Multiplication, Division, Powers and Roots. The dirty little secret is this: There are answers to these operations which can not be found on the ant's number line thread. Consider exponents and roots. When we take the square root of a number, say the square root of 4, the operation is asking a question: What number, multiplied by itself, equals 4? The answer, of course, is two. But wait! That is not the only answer. Do you know what the other answer might be?
The other answer is negative two. Indeed, negative two times negate two equals.... FOUR. So for any root, there are two possible answers. But if that is so, then riddle me this:
What number multiplied by itself equals (4)? What is the square root of negative four? This is a place where the operation bursts off the rails. Either something is wrong with the question, or the number line itself is not complete for it can not handle the answer to such a simple question. No such answer can be found on the ants skinny thread.
Is the number line incomplete? How could that be? It makes no sense, and yet if you push through the nonsense, using these impossible numbers in farther operations, you can wind up with a perfectly good and correct answer at the end of the line. This is what vexed mathematicians for years. They couldn't make heads or tales of these impossible numbers, so they couldn't trust them, no matter how handy the final answers might be. These numbers were dismissed as untrustworthy, as fantasy, as IMAGINARY.
The name stuck. In time, mathematicians did figure it out. There really are an infinite number of numbers which are not on the ants number line, and they are every bit as real as any number you can name. These numbers, each individual number, has two parts; a real part and an imaginary part. This is a bit complex, so a much better name for imaginary numbers is to call them 'Complex Numbers'. So where are they, these Complex numbers? They are there, right along side the real numbers. Stand on the ants numberline thread, facing forward or backwards. Turn 90 degrees to the right (or left), step off the number line and walk in that direction. You are now among the complex numbers, and an entire world has opened to you. No longer must you spend your life on a thread. Now you have an infinite plane to walk upon, a plane which contains ALL possible answers for ALL of the mathematical operations you learned about in school. The Complex Plane makes all of the mathematical operations complete.
What are Complex Numbers good for? All sorts of things. Complex Numbers show up throughout the core disciplines of mathematics, physics, electronics, even computer graphics.
In Trigonometry you may have studied ambiguous triangles, in which the dangling side of one edge is too short to reach either of the other two sides. The easy answer we are given in Trigonometry is that there is no solution to ambiguous triangles, but this easy answer is not so. There are answers to that sort of triangle, they just are not found in the real numbers.
Algebra and Calculus must deal with all sorts of imaginary roots. In fact, the nice easy answers on the real number line are the exception, not the rule. Instructors like to offer nice easy problems with nice clean solutions; but this implicit human favoritism often masks the reality that real life is messy.
It is a property of numbers that, when you multiply any number by an imaginary number, you rotate it by 90 degrees. This is very handy for computer graphics, so imaginary numbers can be put to work there.
Perhaps the most widely seen example using imaginary numbers is the rendering of Mandelbrot Sets and Julia sets, those magical mathematical fractal monsters which so mesmerize the eye and twist the mind with their infinite iterated designwithoutadesigner. To those who suspected that Complex Numbers were not fully real, but just a fancy bookkeeping method exploiting the 'real' numbers, the Mandelbrot says 'Not So!'. Every point in the Mandelbrot design is an individual number on the complex plane. Yet when these numbers are dropped into a laughably simple equation and fed back into itself a number of times, the Mandelbrot monster emerges in all its stunning glory. The Mandelbrot lurks on the complex plane and its presence shows that imaginary numbers are very real indeed.
The best part is that imaginary numbers and exploring the Mandelbrot Monster are within your reach. With these library functions GLBasic can handle complex numbers and you can write programs to do the rest. Imaginary numbers are fun.
Cheers!
CW

Indeed, and life would become boring.
I will tinker with your code and see what occurs. Im the type of guy that (sometimes) takes something apart, puts it all back together and finds a few screws left over...cough :)
...and then the universe kindly balances everything out by showing you that those spare screws fit perfectly into the alarm clock you fixed  which until now had a loose case. COSMIC!...