Formula Compiler tutorial 
Introduction to the Fractal Explorer Formula Compiler
version 2.03.02
September 2004


Written by Sirotinsky Arthur, Olga Fedorenko
http://www.eclectasy.com/Fractal-Explorer/index.html
sarthur71@mail.ru

and Denis McCauley
http://ata-tenui.ifrance.com/
 
  Table of content: 
  0     Changes
  1     Acknowledgements
  2     Limitations
  3     Introduction
        3.1   What is the formula compiler ?
        3.2   Formula Files
  4     Formula anatomy
        4.1   Formula is a programme
        4.2   Formula is a Delphi/Pascal programme
        4.3   Requirements
        4.4   Simple project example
        4.5   Temporary variables declaration
        4.6   Advanced project example
        4.7   The complex functions
        4.8   Bailout test
  5     Complex variables functions
        5.1   List of the functions and procedures
        5.2   Functions selector
  6     Examples of the new formula sintax
  7     List of changes
  Changes: 

Version 2.03 has no fundamental differences from version 2.02. The basic achievement of this version is a significant speed improvement. Also, little bug fixed and added the two new functions: CConj and CConjV. Besides the constant CPixel was added. This constant contains the source complex value of the pixel which is currently calculated (i.e. Z0).

The version 2.03 is a further development of the compiler and contains many new complex-variable functions. There are now 30 such functions. But the main innovation is that in the formulae two selectors of functions can be used, operate with which it is possible from the "Select fractal" window. So, now the formulae can be changed without compilation. In the following version we shall try to optimise libraries and to increase speed of calculations.

Version 2.01 of the formula compiler has few but very important changes. First of all, now you do not need to write the difficult expressions for formula realization. We have added support for many operations and functions of complex values. Also, we added four complex variables for using in your functions. In part number [5] we describe these new possibilities. Please, read it before using the formula compiler.

  Asknowledgements:  

Bradley Beacham for

                   FRMTUTOR.TXT
               AN INTRODUCTION TO
           THE FRACTINT FORMULA PARSER

Borland /Inprise/ Corp. for Delphi Command Line compiler

  Limitations:  

Our formula compiler is not a parser. It's a part of the program, which calculates one step of the iteration process. It can't read formula directly, since the complex type is non-standart type for Delphi.

  Introduction:  

3.1 What is the formula compiler ?

It is a new part of the fractal-generating programme, Fractal Explorer (version 1.13 and greater).

FE has many different types of the fractal formulae itself, the formula compiler allows you to add new fractals without having to change the programme. These formulae are stored in fractal spot files, and may be viewed and edited by anuther user.

3.2 Formula Files

New formulae will be not saved to another file. Formula will be stored as fractal spot file (*.frs), which contain internal data for generating of the image. Therefore, FE requires one FRS file for building one image (and the formula is already implemented into the file).

  Formula anatomy:  

4.0 Why a compiler ?

Compiled programmes always work more quickly than interpreter.

However, don't be surprised that the formulae written by you work too slowly. Please remember that the formulae that are built into the programme are specially optimised for calculation speed. Unfortunately, such optimisation is impossible for the formulae connected from the outside. We continue to work with this problem and we hope to solve it in the future. So, you can see, what each new version works quickly then previous. :o)

4.1 Formula it's a program

Fractal Explorer uses a formula's text for building Dinamic Link Libraries (DLL), and after that loads the new DLL (for interest - look to the system TEMP folder: strings "SET TMP=" or "SET TEMP=" in your c:\autoexec.bat). When the formula changed (or FE closed) the DLL will be unloaded from memory and deleted with the project source.

4.2 Formula it's a Delphi/Pascal program

To compile a new formula, FE will create the Delphi project file (*.DPR) in the temporary folder and execute Borland Delphitm Command Line compiler to generate the DLL file.

Since we are using Delphi for writing Fractal Explorer, we are using Delphi for DLL compilation.

4.3 Requirements

To use the Formula Compiler in FE the files listed below are required:

dcc32.exe
math.dcu
sharemem.dcu
sysinit.dcu
system.dcu
sysutils.dcu
windows.dcu
rlink32.dll
sysutils.inc
make.pif

All files must exist in the Fractal Explorer folder. FE will be check for the presence of these files before compiling the new formula. If you have lost one or more files - download full archive from:
          http://www.eclectasy.com/Fractal-Explorer/download.htm
and unzip it into FE's folder.

4.4 Simple project example

Complex numbers are two-part numbers: the first part is thr real part, and thr second part is the imaginary part. The notation of complex numbers is:

Z = X + jY

where "j" - imaginary part attribute (square root from "-1"). Fractal Explorer uses this format for complex numbers.

To illustrate a fractal formula take the Mandelbroth Set:

z_new = z * z + c
You write in the edit box:
    var k: Extended;
  Begin
    k:=(x+y) * (x-y) + cr;
    y:=2 * x * y + ci;
    x:=k;
  End;

"x" is the real part of "z" complex number, and "y" the imaginary part. "cr" is the real part of "c" complex number, and "ci" the imaginary part. "z" is the value received in the "x" and "y" variables. New value of "z" will be returned in these variables.

The variable "k" is used to store the new "x" values before calculating new "y" values.

Press the "Compile" button and FE will make a Delphi project for this formula:

  library proba;
   Uses ShareMem, SysUtils, Math;
   Procedure Formula(  var x, y, cr, ci: Extended;
                     const p1r, p1i, p2r, p2i, p3r, p3i, p4r, p4i: Extended;
                       var cA1, cA2, cA3, cA4: TComplex;
                     const CPixel: TComplex;
                     const Fn1, Fn2: Integer); export;
   var k: Extended;
  Begin
    k:=(x+y) * (x-y) + cr;
    y:=2 * x * y + ci;
    x:=k;
  End;
  exports
   Formula index 1 name 'Formula';
  BEGIN
  END.

For Delphi and Pascal programmers this code are simple. Other peoples may be interested only in Procedure Formula declaration:

  Procedure Formula(  var x, y, cr, ci: Extended;
                    const p1r, p1i, p2r, p2i, p3r, p3i, p4r, p4i: Extended;
                      var cA1, cA2, cA3, cA4: TComplex;
                    const CPixel: TComplex;
                    const Fn1, Fn2: Integer); export;

Variables:
x, y -mathematical coordinates of current point on every cycle (IN/OUT);
cr,ci -real and imaginary parts of Julia parameter, usually called «C» (IN/OUT);
p1r..p4i -real and imaginary parts of fractal parameters P1, P2, P3 and P4 (IN);
cA1..cA4 -zero-initialized variables available for use by you (IN/OUT);
CPixel -constant value, which contains the complex coordinates of the current pixel (IN);
Fn1, Fn2 -codes of the first and second functions (IN).

You can modify values of cr and ci variales in your formulae. There are lot of the good formulas used this feature. For example:

    var c1,c2: Extended;
  Begin
    c1:=x; c2:=y;
    k:=(x+y) * (x-y) + cr;
    y:=2 * x * y + ci;
    x:=k;
    cr:=c1; ci:=c2;
  End;

Values, received in p1r, p1i, .. , p4r, p4i parameters are real and imaginary parts of the P1, P2, P3 and P4 parameters of the fractal (look to the "Select formula" window). They are constant for your formula. You can't modify values of these variables. But you can use cA1,..cA4 variables to send the data from one iteration to another. This variabels available for modifing and they are initialized to zero before each pixel calculation.

That's all, folks !

NOTE: do not write calculation cycle; Fractal Explorer does it itself!!!

4.5 Temporarly variable declaration

Separate your formula into small parts and use temporary variables (remember, that it requires the "var" section). Sintax for a temporary variable declaration:

var name1, name2, name3: [type]

Type may be "Single", "Double", "Extended" or "TComplex".

Comparsion of those types:
type range significant digits size in bytes
Single 1.5x10-45 .. 3.4x1038 7..8 4
Double 5.0x10-324 .. 1.7x10308 15..16 8
Extended 3.6x10-4951 .. 1.1x104932 19..20 10


«Single» works quickly, but precision is lowest;
«Double» works much slowly, but precision is good;
«Extended» works very slowly, but precision is very good.

The «Double» type is used by default and we recommend it to you.

The special type for the complex numbers is declared in the Formula Compiler:

  TComplex = record
    real: Extended;
    imag: Extended;
  end;

You can declare variables of this type in your formulae to store complex numbers and use internal functions and procedures. By the way, variables cA1, cA2, cA3 and cA4 has TComplex type too.

Examles:

    var tmp1,tmp2: Double;
  Begin
    <>formula...<>
  End;
  
var h1x,h1y: Double; h2x,h2y: Double; Begin <>formula...<> End;
var h1x,h1y: Double; h2x,h2y: Double; f: Extended; c1,c2: TComplex; Begin <>formula...<> c1:=c2; // -it is a totally right operation <>formula...<> End;

4.6 Advanced project example

Mandel/Talis formula:

z_new = z*z + c;
c_new = c*c/(c+P1) + z_new;

Write in the edit area:

    var h1x,h1y, h2x,h2y, f: Extended;  // -temporary variables
  Begin
    h1x:=(x+y)*(x-y)+cr;                // -calculating z_new
    y  := x*y*2     +ci;                //  z_new = z*z + c
    x  := h1x;

    h1x:= (cr+ci)*(cr-ci);              // -h1 = c*c
    h1y:=  cr*ci * 2;

    h2x:= cr+p1r;                       // -h2 = c+P1
    h2y:= ci+p1i;

    f  := h2x*h2x+h2y*h2y+1E-10;        // -c_new=h1/h2 + z_new
    cr :=(h2x*h1x+h2y*h1y)/f+x;
    ci :=(h2x*h1y-h2y*h1x)/f+y;
  End;

Calculate new values for "x" (real part) and "y" (imaginary part) of complex variable "z".

Also, change cr and ci values - real and imaginary parts of "c". Next step will use z_new and c_new.

4.7 The complex functions

You can use any function of the complex variables in your formula, but you must describe them, like this (remember, that Z = X + jY and C = CR + jCI):

    z*z:       
       X_new := (X-Y)*(X+Y);
       Y_new := 2 * X*Y;

    z*c:       
       X_new := X*CR - Y*CI;
       Y_new := X*CI + Y*CR;

    z*z*z:       
       X_new := X*(X*X-3*Y*Y);                       // -this is optimized formula
       Y_new := Y*(3*X*X-Y*Y);
                                                     
    z*z*z*z:                             
       tmp_r := (X-Y)*(X+Y);                          
       tmp_i := 2 * X*Y;                             
       X_new := (tmp_r - tmp_i) * (tmp_r + tmp_i);   
       Y_new := 2 * tmp_r * tmp_i;           
                                                    
    1/z:                         
       tmp   := X*X + Y*Y + 1E-25;                   // -without 1E-25 may by
       X_new := X / tmp;                             //  divizion by zero
       Y_new :=-Y / tmp;                     

    1/(z*z):       
       tmp   := X*X + Y*Y;
       tmp   := tmp * tmp + 1E-25;                   // -see above
       X_new := (X*X - Y*Y) / tmp;
       Y_new := (2*X * Y  ) / tmp;

    Sqrt(z):       
       tmp   := Sqrt(X*X + Y*Y);
       X_new := Sqrt(Abs((X + tmp)/2));
       Y_new := Sqrt(Abs((tmp - X)/2));

    Exp(z):
       tmp   := Exp(X);                              // e^z
       X_new := tmp * Cos(Y);
       Y_new := tmp * Sin(Y);

    Exp(1/z):
       tmp   := X*X + Y*Y + 1E-25;                   // -see above
       s1    := X/tmp;
       s2    :=-Y/tmp;
       tmp   := Exp(s1);
       X_new := tmp * Cos(s2);
       Y_new := tmp * Sin(s2);

    Ln(z):       
       X_new :=Log2(X*X+Y*Y)/2.7182818285;
       Y_new :=ArcTan2(Y,X);

    z^c:       
       h1x   :=Log2(X*X+Y*Y)/2.7182818285;           // -Ln(z)
       h1y   :=ArcTan2(Y,X);
       h2x   :=h1x*CR - h1y*CI;                      // -Ln(z)*c
       h2y   :=h1y*CR + h1x*CI;
       f     :=Exp(h2x);                             // -Exp(Ln(z)*c)
       X_new :=f*Cos(h2y);
       Y_new :=f*Sin(h2y);

    Sin(z):       
       tmp   := Exp(Y)/2;                            // -optimized formula (!)
       tmp2  := 0.25/tmp;
       X_new := Sin(X) * (tmp+tmp2);
       Y_new := Cos(X) * (tmp-tmp2);

    Cos(z):       
       X_new := Cos(X)*Cosh(Y);                      // -not optimized formula
       Y_new :=-Sin(X)*Sinh(Y);

    Tan(z):       
       X_new := Sin(2*X)/(Cos(2*X)+Cosh(2*Y));
       Y_new := Sin(2*Y)/(Cos(2*X)+Cosh(2*Y));

    Sinh(z):                      // -hiperbolic sinus
       X_new := Sinh(X)*Cos(Y);                      // -it is not optimized formula
       Y_new := Cosh(X)*Sin(Y);

    Cosh(z):                      // -hiperbolic cosinus
       X_new := Cosh(X)*Cos(Y);                      // -it is not optimized formula
       Y_new := Sinh(X)*Sin(Y);
  

You must change X, Y, CR and CI variables to those, which required by you formula.

4.8 Bailout test

A new feature has been included in the Formula Compiler in Fractal Explorer 1.24 beta 1. In addition to the two main bailout tests (M-set and N-set) you can add your custom tests to the bailout function.

What is a difference between M-Set and N-Set?

The M-Set bailout test performs an exit from the loop when the modulus squared of the complex number exceeds the specified value. For example, |Z|>4 performs the loop again and again while |Z| is less 4. Once |Z| exceeds 4 the calculation loop will stop. This is a classical Mandelbrot-like bailout test; fractals based on this algorithm are sometimes called "Escape-Time To Infinity" fractals.

The N-Set bailout test is a fundamentally different test. This test checks the difference between the current iteration and the precedent iteration. A classical example of the N-Set test is: |Z-Zold|<0.0001.

Such fractals are sometimes called "Escape-Time To Finity" fractals, and the N-Set bailout test is sometimes called "F-Set" (finity-set).

The N-set calculation loop will finish when the modulus squared of the difference between the current value and the previous value is less than 0.0001. To perform the N-Set test we should pass the value of Z from the current cycle to the next.

The common algoritm of the N-Set is listed below:

      1. calculate new Z;
      2. perform a bailout test;
      3. save Z to another variable;
    var OldZ: TComplex
  Begin
    <..> 
    If iter_number=1 Then 
       OldZ:=MakeComplex(0,0);
 
    Z:=Z-(Z^2+1)/(2*Z);
    
    If |Z-OldZ|<0.0001 Then EndLoop:=True;  
    OldZ:=Z; 
  End; 

In the init section the OldZ variable is set to zero. The next strings are calculation of the loop. First - calculate the new value of Z; second - perform the N-Set bailout test and set the "EndLoop" variable to "True" if the test is passed. The last string of the code saves the current Z value to the OldZ variable and sends this value to the next cycle.

Formula Compiler implementation:

To perform the user-defined bailout tests you may use global variable

var EndLoop: Boolean;
.

Before the loop this variable is set to FALSE. If the bailout test is passed - you may set EndLoop to TRUE:

EndLoop:=True;

Remember that if the answer of a bailout test is "FALSE", the loop must be performed again; otherwise, it is time to quit iterating.

In addition, a new function to calculate the squared modulus of the complex number has been added:

CModule(complex: TComplex);

Let us check an example to learn more about user-defined bailout tests:

    var Z,C,P1   : TComplex;
        T1,T2,T3 : TComplex;
  Begin                         
    Z :=MakeComplex(x,y);       
    C :=MakeComplex(cr,ci);     
    P1:=MakeComplex(p1r,p1i);   
                              
    // CGNewtonSinExp by Chris Green
    //   z1=exp(z),
    //   z2=sin(z)+z1-z,
    //   z=z-p1*z2/(cos(z)+z1),
    T1:=CExp(Z);
    T2:=CSub(CAdd(CSin(Z),T1),Z);
    T3:=CAdd(CCos(Z),T1);
    Z :=CSub(Z,CDiv(CMul(P1,T2),T3));
                             
    SetResult(x,y, Z);          
                              
    // bailout test: .000001 < |z2|
    If CModule(T2)<0.000001 Then           
       EndLoop:=True;           
  End;
  Functions of complex variables:  

5.1 List of the functions and procedures

The special type for storing of the complex numbers was described above. You need to remember this declaration:

       Type
         TComplex = record
           real: Extended;
           imag: Extended;
         end;

All of the functions listed below will use this type for their operations. It's a very important part of the new version Formula Compiler.

List of the functions and procedures

     I. Function  MakeComplex(real, imag: Extended): TComplex;
                  This function makes a TComplex value from the two values,
                  which describe the real and imaginary parts of the
                  complex value. Use this function for translate the input values
                  (x,y, cr,ci and p1r..p4i) to the complex type. After 
                  translation you can use result in another functions
                  and procedures.
                  Also, you can use this function to make a TComplex value
                  from a constant, for example:
                     
                     tmp:=MakeComplex(3,0) - convert the real number "3" into 
                                             TComplex type.
                     
    II. Procedure SetResult(var x,y: Extended; const complex: TComplex);
                  This procedure makes final results accessible for the 
                  Formula Compiler. As described in the part [4.4], the result 
                  values must be returned in the "X" and "Y" variables.
                  You can use two operations for it:
                      x:=Z.real;
                      y:=Z.imag;
                  but now you can use the SetResult procedure for this.
                  
                  Note: remember, what the SetResult procedure works
                        little bit slowly then direct storing the data.
   
       Below are listed functions and procedures for mathematical operations.
   Every operation has two releases: first, as a procedure with no result
   returned, and second, as a function, which returns the result of the
   operation as a TComplex value.  Any procedure keeps the resulting complex
   value in the variable which passed as the first parameter.


   III. Sum of the two complex:
        Procedure CAddV(var Cmp1: TComplex; const Cmp2: TComplex);
                  Cmp1 := Cmp1 + Cmp2;
                  Result value will be returned in the "Cmp1" variable.
 
        Function  CAdd (const Cmp1, Cmp2: TComplex): TComplex;
                  Cmp3 := Cmp1 + Cmp2;
                  Result value will be returned as a result of the function.

        Function  CAddR (const Cmp1: TComplex; t: Extended): TComplex;
                  Cmp2 := Cmp1 + t
                  Added real value "t" to the "Cmp1" complex.

    IV. Subtraction of the two complex:
        Procedure CSubV(var Cmp1: TComplex; const Cmp2: TComplex);
                  Cmp1 := Cmp1 - Cmp2;

        Function  CSub (const Cmp1, Cmp2: TComplex): TComplex;
                  Cmp3 := Cmp1 - Cmp2;

        Function  CSubR (const Cmp1: TComplex; t: Extended): TComplex;
                  Cmp2 := Cmp1 - t;

     V. Multiplication of the two complex:
        Procedure CMulV(var Cmp1: TComplex; const Cmp2: TComplex);
                  Cmp1 := Cmp1 * Cmp2;

        Function  CMul (const Cmp1, Cmp2: TComplex): TComplex;
                  Cmp3 := Cmp1 * Cmp2;

        Function  CMulR (const Cmp1: TComplex; t: Extended): TComplex;
                  Cmp2 := Cmp1 * t;  
                         (Cmp2.real = Cmp1.real*t
                          Cmp2.imag = Cmp1.real*t)

    VI. Division of the two complex:
        Procedure CDivV(var Cmp1: TComplex; const Cmp2: TComplex);
                  Cmp1 := Cmp1 / Cmp2;

        Function  CDiv (const Cmp1, Cmp2: TComplex): TComplex;
                  Cmp3 := Cmp1 / Cmp2;

        Function  CDivR (const Cmp1: TComplex; t: Extended): TComplex;
                  Cmp2 := Cmp1 / t;
                         (Cmp2.real = Cmp1.real/t
                          Cmp2.imag = Cmp1.real/t)

   The next set of functions realizes the optimized functions

   VII. Square of the complex:
        Procedure CSqrV(var Cmp1: TComplex);
                  Cmp1 := Cmp1^2;

        Function  CSqr (Cmp1: TComplex): TComplex;
                  Cmp2 := Cmp1^2;

  VIII. Third power of the complex:
        Procedure CTripleV(var Cmp1: TComplex);
                  Cmp1 := Cmp1^3;

        Function  CTriple (Cmp1: TComplex): TComplex;
                  Cmp2 := Cmp1^3;

    IX. Fourth power of the complex:
        Procedure CFourV(var Cmp1: TComplex);
                  Cmp1 := Cmp1^4;

        Function  CFour (Cmp1: TComplex): TComplex;
                  Cmp2 := Cmp1^4;

     X. Flip of the complex:
        Procedure CFlipV(var Cmp1: TComplex);
                  Cmp1.real := Cmp1.imag;
                  Cmp1.imag := Cmp1.real;

        Function  CFlip (Cmp1: TComplex): TComplex;
                  Cmp2.real := Cmp1.imag;
                  Cmp2.imag := Cmp1.real;

    XI. Reversing of the complex value:
        Procedure CRevV (var Cmp1: TComplex);
                  Cmp1 := 1/ Cmp1;

        Function  CRev  (const Cmp1: TComplex): TComplex;
                  Cmp2 := 1/ Cmp1;

   XII. Another reversing:
        Procedure CRev2V(var Cmp1: TComplex; Cmp2: TComplex);
                  Cmp1 := 1 / (Cmp1 - Cmp2);

        Function  CRev2 (Cmp1,Cmp2: TComplex): TComplex;
                  Cmp3 := 1 / (Cmp1 - Cmp2);

  XIII. Absolute value of complex:
        Procedure CAbsV(var Cmp1: TComplex);
                  Cmp1.real := Abs(Cmp1.real);
                  Cmp1.imag := Cmp1.imag;
        Function  CAbs (Cmp1: TComplex): TComplex;
                  Cmp2.real := Abs(Cmp1.real);
                  Cmp2.imag := Cmp1.Imag;
       
        Procedure CAbs2V(var Cmp1: TComplex);
                  Cmp1.real := Abs(Cmp1.real);
                  Cmp1.imag := Abs(Cmp1.imag);
        Function  CAbs2 (Cmp1: TComplex): TComplex;
                  Cmp2.real := Abs(Cmp1.real);
                  Cmp2.imag := Abs(Cmp1.Imag);

   XIV. Real or imag part of complex:
        Procedure CRealV(var Cmp1: TComplex);
                  Cmp1.real := Cmp1.real;
                  Cmp1.imag := 0; 
        Function  CReal (Cmp1: TComplex): TComplex;
                  Cmp2.real := Cmp1.real;
                  Cmp2.imag := 0;  

        Procedure CImagV(var Cmp1: TComplex);
                  Cmp1.real := Cmp1.imag;
                  Cmp1.imag := 0; 
        Function  CImag (Cmp1: TComplex): TComplex;
                  Cmp2.real := Cmp1.imag;
                  Cmp2.imag := 0;  

    XV. Squre root of the complex:
        Procedure CSqrtV(var Cmp1: TComplex);
                  Cmp1 := Sqrt(Cmp1);

        Function  CSqrt (Cmp1: TComplex): TComplex;
                  Cmp2 := Sqrt(Cmp1);

   XVI. Exponent of the complex:
        Procedure CExpV (var Cmp1: TComplex);
                  Cmp1 := Exp(Cmp1);

        Function  CExp  (Cmp1: TComplex): TComplex;
                  Cmp2 := Exp(Cmp1);

  XVII. Logorithm of the complex:
        Procedure CLnV  (var Cmp1: TComplex);
                  Cmp1 := Ln(Cmp1);

        Function  CLn   (Cmp1: TComplex): TComplex;
                  Cmp2 := Ln(Cmp1);

 XVIII. Raise complex to a real power
        Procedure CPowerRV(var Cmp1: TComplex; t: Extended);
                  Cmp1 := Cmp1^t;

        Function  CPowerR (const Cmp1:TComplex;t: Extended): TComplex;
                  Cmp2 := Cmp1^t;

        Procedure CPowerRV2(var Cmp1: TComplex; t: Extended);
                  Cmp1 := Cmp1^t;

        Function  CPowerR2(const Cmp1:TComplex;t: Extended): TComplex;
                  Cmp2 := Cmp1^t;

   XIX. Raise complex value to a complex power:
        Procedure CPowerV(var Cmp1: TComplex; Cmp2: TComplex);
                  Cmp1 := Cmp1^Cmp2;

        Function  CPower (Cmp1,Cmp2: TComplex): TComplex;
                  Cmp3 := Cmp1^Cmp2;

        Procedure CPowerV2(var Cmp1: TComplex; Cmp2: TComplex);
                  Cmp1 := Cmp1^Cmp2;

        Function  CPower2(Cmp1,Cmp2: TComplex): TComplex;
                  Cmp3 := Cmp1^Cmp2;


   Trigonometric functions

    XX. Sinus of the complex:
        Procedure CSinV (var Cmp1: TComplex);
                  Cmp1 := Sin(Cmp1);

        Function  CSin  (Cmp1: TComplex): TComplex;
                  Cmp2 := Sin(Cmp1);

   XXI. Cosinus of the complex:
        Procedure CCosV (var Cmp1: TComplex);
                  Cmp1 := Cos(Cmp1);

        Function  CCos  (Cmp1: TComplex): TComplex;
                  Cmp2 := Cos(Cmp1);

  XXII. Tangent of the complex:
        Procedure CTanV (var Cmp1: TComplex);
                  Cmp1 := Tan(Cmp1);

        Function  CTan  (Cmp1: TComplex): TComplex;
                  Cmp2 := Tan(Cmp1);

 XXIII. Cotangent of the complex:
        Procedure CCotanV (var Cmp1: TComplex);
                  Cmp1 := Cotan(Cmp1);

        Function  CCotan  (Cmp1: TComplex): TComplex;
                  Cmp2 := Cotan(Cmp1);

  XXIV. Hiperbolic sinus of the complex:
        Procedure CSinhV(var Cmp1: TComplex);
                  Cmp1 := Sinh(Cmp1);

        Function  CSinh (Cmp1: TComplex): TComplex;
                  Cmp2 := Sinh(Cmp1);

   XXV. Hiperbolic cosinus of the complex:
        Procedure CConhV(var Cmp1: TComplex);
                  Cmp1 := Cosh(Cmp1);

        Function  CCosh (Cmp1: TComplex): TComplex;
                  Cmp2 := Cosh(Cmp1);

  XXVI. ArcSinus of the complex:
        Procedure CASinV (var Cmp1: TComplex);
                  Cmp1 := ArcSin(Cmp1);

        Function  CASin  (const Cmp1: TComplex): TComplex;
                  Cmp2 := ArcSin(Cmp1);

 XXVII. ArcCosinus of the complex:
        Procedure CACosV (var Cmp1: TComplex);
                  Cmp1 := ArcCos(Cmp1);

        Function  CACos  (const Cmp1: TComplex): TComplex;
                  Cmp2 := ArcCos(Cmp1);

XXVIII. ArcTangens of the complex:
        Procedure CATanV (var Cmp1: TComplex);
                  Cmp1 := ArcTan(Cmp1);

        Function  CATan  (const Cmp1: TComplex): TComplex;
                  Cmp2 := ArcTan(Cmp1);

  XXIX. Gyperbolic arcsinus of the complex:
        Procedure CASinhV (var Cmp1: TComplex);
                  Cmp1 := ArcSinh(Cmp1);

        Function  CASinh  (const Cmp1: TComplex): TComplex;
                  Cmp2 := ArcSinh(Cmp1);

   XXX. Hyperbolic arccosinus of the complex:
        Procedure CACoshV (var Cmp1: TComplex);
                  Cmp1 := ArcCosh(Cmp1);

        Function  CACosh  (const Cmp1: TComplex): TComplex;
                  Cmp2 := ArcCosh(Cmp1);

  XXXI. Hyperbolic arctangent of the complex:
        Procedure CATanhV (var Cmp1: TComplex);
                  Cmp1 := ArcTanh(Cmp1);

        Function  CATanh  (const Cmp1: TComplex): TComplex;
                  Cmp2 := ArcTanh(Cmp1);

   Additional functions
XXXII.  Changes sign of imaginary part of the complex value
                  Result.real := Cmp1.real;
                  Result.imag :=-Cmp1.imag;
        Procedure CConjV (var Cmp1: TComplex);
        Function  CConj  (const Cmp1: TComplex): TComplex;

5.2 Functions selectors

From the "Select fractal" window two functions can be changed. These changes does not require re-compilation of the formula. You can use in your formula one or two functions and change the fractal directly from the "Select fractal" dialogue.

To support external functions selections the procedure FuncDisp is implemented:

Procedure FuncDisp(const Fn: Integer; var Cmp1: TComplex);

In this procedure - Fn parameter is Fn1 or Fn2 variables, which is passed to formulae from the outside. For example, write in your formula:

FuncDisp(Fn1, Z);

And you can change the first function to get changes in the formula.

Examples of the Mandelbrot-set with functions selections:

    var Z,Z0: TComplex;
  Begin
    FuncDisp(Fn1, Z);              // change source value of the Z variable
    Z :=CAdd( CSqr(Z), Ñ);         // calculate the Mandel

    SetResult(x,y, Z);             // store result to [x] and [y] variables
  End;

FuncDisp is a procedure, which modify second parameter. In the many cases such modification is not good and formula writers needs to add additional commands for saving previous value of the modified variable. To reduce time of the formula executing the new function dispatcher was added:

Function FuncDispF(const Fn: Integer; Cmp1: TComplex): TComplex;

Like FuncDisp procedure Fn is a selector number and can assume one of the two values: Fn1 or Fn2. Cmp1 is a complex variable, which is an argument of the selected function. Value of the Cmp1 does not changed. The example code, listed above, can be written as:

  Begin
    Z :=CAdd( CSqr( FuncDispF(Fn1, Z) ), Ñ); 
    SetResult(x,y, Z);            
  End;
  Examples of the new formula sintax:  
  I. Mandelbrot formula:

     
       var Z,Z0: TComplex;
     Begin
       Z :=MakeComplex(x,y);          // make complex from [x] and [y] variables
       Z0:=MakeComplex(cr,ci);        // make complex from [cr] and [ci] variables

       Z :=CAdd( CSqr(Z), Z0);        // calculate the Mandel

       SetResult(x,y, Z);             // store result to [x] and [y] variables
     End;
     

 II. Mandel/Talis formula:
       z_new = z*z + c;
       c_new = c*c/(c+P1) + z_new;

     Write:

     
       var Z,Z0,P1: TComplex;
     Begin
       Z :=MakeComplex(x,y);          // make complex from [x] and [y] variables
       Z0:=MakeComplex(cr,ci);        // make complex from [cr] and [ci] variables
       P1:=MakeComplex(p1r,p1i);      // make complex from [p1r] and [p1i] variables

       Z :=CAdd( CSqr(Z), Z0);        // calculates Z_NEW

       Z0:=CDiv(CSqr(Z0), CAdd(Z0,P1))   // Z' = C*C/(C+P1)
       Z0:=CAdd(Z0,Z);                   // C_NEW  = Z'+Z_NEW
       SetResult(cr,ci, Z0);          // store C_NEW to [cr] and [ci] variables
       SetResult(x,y, Z);             // store Z_NEW to [x] and [y] variables
     End;
     

Others, more difficult examples can be found in the FE.ZIP archive. They're placed into the "Formulas" subfolder.

  List of changes:  

Changes in versions 2.01 and 2.02


[+] added function selection support (look at [5.2] for details and example);
[+] errors in some functions fixed;
[+] added new procedures and functions:

  Procedure CAddR(Cmp1: TComplex; t: Extended): TComplex;
  Function  CSubR(Cmp1: TComplex; t: Extended): TComplex;
  Procedure CMulR(Cmp1: TComplex; t: Extended): TComplex;
  Function  CDivR(Cmp1: TComplex; t: Extended): TComplex;

  Procedure CPowerRV(var Cmp1: TComplex; t: Extended);
  Function  CPowerR (    Cmp1: TComplex; t: Extended): TComplex;

  Procedure CCotanV(  var Cmp1: TComplex);
  Function  CCotan (const Cmp1: TComplex): TComplex;

  Procedure CASinV (  var Cmp1: TComplex);
  Function  CASin  (const Cmp1: TComplex): TComplex;
  Procedure CACosV (  var Cmp1: TComplex);
  Function  CACos  (const Cmp1: TComplex): TComplex;
  Procedure CATanV (  var Cmp1: TComplex);
  Function  CATan  (const Cmp1: TComplex): TComplex;

  Procedure CASinhV(  var Cmp1: TComplex);
  Function  CASinh (const Cmp1: TComplex): TComplex;
  Procedure CACoshV(  var Cmp1: TComplex);
  Function  CACosh (const Cmp1: TComplex): TComplex;
  Procedure CATanhV(  var Cmp1: TComplex);
  Function  CATanh (const Cmp1: TComplex): TComplex;


[+] added four complex variables for using in your formulae. new declaration of the main procedure listed below:

  Procedure Formula(var x, y, cr, ci: Extended;
                  const p1r, p1i, p2r, p2i, p3r, p3i: Extended;
                    var cA1, cA2, cA3, cA4: TComplex;
                  const Fn1, Fn2: Integer); export; 


These variables are initialised to zero values, i.e. cA1.real = 0 and cA1.imag = 0 and so on. During calculation of the one point these variables will keep user values.

[!] now N(ewton)-Set Method will work properly;

Changes in version 2.03


[++] added FEParser by Kyle McCord to help you to write the formulas simple;
[+] speed improvements;
[+] little bug fixes;
[+] added the new CPixel constant and P4 parameter:

  Procedure Formula(var x, y, cr, ci: Extended;
                  const p1r, p1i, p2r, p2i, p3r, p3i, p4r, p4i: Extended;
                    var cA1, cA2, cA3, cA4: TComplex;
                  const CPixel: TComplex;
                  const Fn1, Fn2: Integer); export; 


[+] added new procedure and function:

  Procedure CConjV(Cmp1: TComplex);
  Function  CConj(Cmp1: TComplex): TComplex;

Jule 23, 2002:


[+] the declaration and initialization of the complex variables Z, C, P1, P2, P3 and P4 has been moved into the parent level. Please, do not worry when you will see the formula pattern without "MakeComplex" strings. ;-) Of course, all listed variables are available for using, and all old formulas are compatible with described changes.

September 26, 2004:


[+] new «formula dispatcher» added: FuncDispF

January 12, 2005:


[+] added fixed versions of the «Complex Power» operation. Previous versions does not removed to keep compatibility.

  Procedure CPowerV2(var Cmp1: TComplex; Cmp2: TComplex);  
  Function  CPower2 (Cmp1,Cmp2: TComplex): TComplex;       

  Procedure CPowerRV2(var Cmp1: TComplex; t: Extended);    
  Function  CPowerR2 (Cmp1:TComplex;t: Extended): TComplex;

 
 
Copyright © 1999-2004
Sirotinsky Arthur, Olga Fedorenko, Denis McCauley
Ukraine, France