FRACTINT'S NEW FLOW CONTROL INSTRUCTIONS FOR THE FORMULA PARSER Version 19.6 of Fractint provides for flow control statements in user created formulas. This feature should make formulas more readable, and preliminary testing indicates that more complicated formulas will run as much as 2 to 3 times faster when the formula is written using the new instructions. This tutorial provides an overview of the new feature. It assumes that the reader is familiar with the syntax of Fractint formulas. If you are new to Fractint, the documentation accompanying Fractint provides a good start, and Bradley Beacham's tutorial (available in Library 3 of the CompuServe Graphics Developers Forum as frmtut.zip, and at Noel Giffen's Fractint web page - http://spanky.triumf.ca) will provide a more detailed explanation of Fractint formula writing. Sylvie Gallet's tutorial on PHC and PTC formulas is a more advanced text, and is available at the same sites. These tutorials are also included in the Fractint v. 19.6 package. Until Fractint v. 19.6, each section of a Fractint formula was always executed one instruction at a time, sequentially through the section without exception. Formula writers worked out ingenious solutions to achieve different results in a formula based on a testing of some condition. For example, if the writer wanted c to be equal to sin(z) if x==y, and cos(z) if not, the formula would contain the following line: c = sin(z)*(x==y) + cos(z)*(x!=y) Each time this line is reached in the formula, which in the course of drawing an image could be many millions of times, each element of the line is computed, so both the x==y and the x!=y tests are evaluated (one of which, of course, will be 0 and the other 1), and both sin(z) and cos(z) are computed. Obviously, it would be much more efficient to evaluate only x==y, and then compute only sin(z) or cos(z) depending on the result. THE NEW FLOW CONTROL INSTRUCTIONS Four new formula instructions are now permitted in a Fractint formula. They are: IF (expression) ELSEIF (expression) ELSE ENDIF Each of these instructions must be a separate statement in the formula, i.e. must end with a comma or an end-of-line character. The easiest way to avoid problems with this requirement is to put each of these instructions on a separate line. The practice of the people testing the new feature has been to capitalize the flow control instructions and to indent the regular calculations which follow them. A group of these instructions beginning with IF and ending with ENDIF constitutes a unit which I'll refer to as an "if block" of instuctions. A formula may contain any number of if blocks, and if blocks may be nested inside other if blocks. The only limitation is that the end of the initialization section (represented by a ":" in the formula) cannot be inside an if block. An if block contains exactly one IF, exactly one ENDIF, no more than 1 ELSE (there may be none), and any number of ELSEIFs (there may be none), but all ELSEIFs must precede the block's ELSE instruction. The format is therefore as follows: IF (expression) statements ELSEIF (expression) statements ELSEIF (expression) statements . . ELSE statements ENDIF ELSEIF (expression) is the equivalent of two flow control instructions, an ELSE immediately followed by an IF (). The difference between the single and the separated instructions is that ELSEIF does not require its own ENDIF, whereas ELSE followed by IF would require that the separate IF have its own ENDIF. The example above, restated using these instructions, will read as follows: IF (x==y) c = sin(z) ELSE c = cos(z) ENDIF Not only is this formulation intuitively easier to understand than the earlier one, it also runs much faster, because only the elements necessary to make the desired assignment to c are executed. The formula parser does the following with these instructions: 1. Evaluate the expression inside the parentheses of the IF instruction. Boolean expressions in Fractint (expressions which use the operators ==, !=, >, <, >=, <=, && and ||) alway evaluate to (1,0) if the expression is true and (0,0) if the expression is false. The nature of branching instructions is such that the expression in the parentheses of an IF instruction will almost always be boolean, but this is not a requirement. Any expression which evaluates to a single complex number is acceptable. Thus, though unusual, the instruction IF(x+y) is perfectly valid, since the result of x+y is a complex number. 2. Look at the real element, and only the real element, of the expression. If it is nonzero, the expression represents "true". If the real element is zero, the expression represents "false". Thus (1,0), (-1,0), and (3.14, 6) are all "true" results. (0,0), (0,1) and (0,-1) are all "false" results. 3. If the expression is true, proceed to the next statement in the formula without any skipping. 4. If the expression is false, skip to the statement immediately following the next flow control instruction in the if block (of necessity, an ELSE, the ELSE element of an ELSEIF(), or ENDIF), and begin further processing from there. 5. If an ELSE or the ELSE portion of an ELSEIF() instruction is reached in the course of processing, jump to the instruction following the ENDIF at the end of that if block. Looking at the above example, the parser first evaluates the expression x==y. If it is true, the next statement, c = sin(x) is executed, and then, the ELSE now being reached, execution jumps to the ENDIF. If x==y is false, execution jumps straight to the statement after the ELSE, which is c = cos(x), and executes it, continuing on from there. With a little reflection you will realize that only one of the sets of statements following flow control instructions in an if block is executed. For example, IF (expr1) group1_statements ELSEIF (expr2) group2_statements ELSEIF (expr3) group3_statements ELSE group4_statements ENDIF If expr1 is true, the group1_statements will be executed and then the execution will jump to the ENDIF. The fact that expr2 or expr3 may be true is irrelevant; the conditions will not be tested if expr1 is true. If expr1 is false, then expr2 will be tested, and if true the group2_statements will be executed, and when done the execution will jump to the ENDIF. expr3 is only evaluated if expr1 and expr2 are both false. You can also see that the group4_statements will only be executed if all three of the test expressions are false. If there is no ELSE statement in an if block, it is possible that none of the calculation statements in the block will be executed. For example, IF (expression) statements ENDIF will cause the statements to be executed only if the expression is true; otherwise they are skipped entirely. EXAMPLE Carr2821 is one of Bob Carr's many formulas which make use of the formula branching technique developed by Sylvie Gallet. It has received much attention lately because of Lee Skinner's marvelous collection of 186 images derived from the formula. Following is the original formula and an IF..ELSE rewrite of it. In addition to the straightforward switch from the old method of branching to the new, you will see that the flow control instructions make much easier the implementation of other short cuts which speed up execution. The IF..ELSE rewrite produces the same images as the original, but in about 1/2 the time, depending on your processor. Carr2821 {; Modified Sylvie Gallet frm. [101324,3444],1996 ; passes=1 needs to be used with this PHC formula. b5=pixel-conj(0.1/pixel) b4=pixel-flip(0.1/pixel)-conj(0.001/pixel), c=whitesq*b4-(whitesq==0)*b4 z=whitesq*b5-(whitesq==0)*b5 c1=1.5*z^1.2,c2=2.25*conj(z),c3=3.375*flip(z),c4=5.0625*flip(z), l1=real(p1),l2=imag(p1),l3=real(p2),l4=imag(p2), bailout=16,iter=0: t1=(iter==l1),t2=(iter==l2),t3=(iter==l3),t4=(iter==l4), t=1-(t1||t2||t3||t4),z=z*t,c=c*t+c1*t1+c2*t2+c3*t3+c4*t4, z=(|z|/5)+z*z+c-0.09/pixel iter=iter+1 |z|<=bailout } carr2821 {; Modified Sylvie Gallet frm. [101324,3444],1996 ; Converted to if.else by Sylvie Gallet and George Martin 3/97 ; passes=1 needs to be used with this PHC formula. pixinv = 0.1/pixel p9 = 0.9*pixinv imagp1 = imag(p1) imagp2 = imag(p2) IF (whitesq) z = zorig = pixel - conj(pixinv) c = pixel - flip(pixinv) - conj(0.01*pixinv) - p9 mz = |z| ELSE z = zorig = conj(pixinv) - pixel c = flip(pixinv) + conj(0.01*pixinv) - pixel - p9 mz = |z| ENDIF bailout = 16 iter = 0 : IF (iter==p1) z = mz = 0 c = 1.5*zorig^1.2 - p9 ELSEIF (iter==imagp1) z = mz = 0 c = 2.25*conj(zorig) - p9 ELSEIF (iter==p2) z = mz = 0 c = 3.375*flip(zorig) - p9 ELSEIF (iter==imagp2) z = mz = 0 c = 5.0625*flip(zorig) - p9 ENDIF z = mz*0.2 + z*z + c mz = |z| iter = iter + 1 mz <= bailout } REWRITING OLD FORMULAS There are more than 1500 existing Fractint formulas which could be written in IF..ELSE format. If you have occasion to rewrite one, you should test the rewrite to make sure it is drawing the identical images that the original version did. To perform this test, do the following: 1. Using a .par file image entry which uses the formula, render the image using the old formula, and save the image (e.g. as oldimage.gif). 2. Using the same .par file image entry, render the image using the rewritten formula, and save the image (e.g. as newimage.gif). 3. Exit Fractint, and restart with the following command line options selected: debug=50 filename=oldimage.gif 4. With oldimage.gif on the screen, hit and select newimage.gif to restore to the screen. Hit 5. The effect of debug=50 is that the pixels which will be shown in the second restored image will only be those which are different than the image previously on the screen. If the images are identical, you will have a completely blank screen. 6. Differences are saved to an ASCII file "cmperr". Exit Fractint and check this file to assure yourself that the two images were in fact identical. Images drawn in floating point mode on a computer with a math coprocessor or a 486 or higher processor may have slight pixel variation between an original and an if..else rewrite because of certain optimizations implemented in the "fast parser" code. If you are seeing these variations and want to make sure the rewritten formula is accurate, draw the images using the original and rewritten formulas with the command line parameter debug=322 selected. This causes Fractint to skip the optimization code, and differences caused by the optimizer will therefore be eliminated. If you still have differences and believe your rewrite is accurate, try again with debug=90 selected. This eliminates the use of the fast parser entirely, and the original formula and your rewrite, if correct, should produce identical images. If you are satisfied that you have a good rewrite, post the revised formula in a message in the Fractint forum on CompuServe, or send an email with the revised formula to me (76440.1143@compuserve.com). It will be helpful if the message includes both the old and rewritten formula, and a .par image entry using the formula so that we can easily verify the accuracy of the rewrite. Good rewrites will be added to the orgform compilation of formulas in place of the old formula, with credit given to the author of the rewrite. Thanks to Sylvie Gallet and Les StClair for their help in writing this tutorial. George Martin 3/23/97