Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Examining Zortech C++ 2.0


JAN90: EXAMINING ZORTECH C++ 2.0

This article contains the following executables: ZGTEST.C ZG LWLVL.C ZG LWLVL.H ZG LWLVL.DOC

Scott is a free-lance writer and software developer; he has over 15 years experience in a variety of programming languages. Scott can be reached either at 705 W. Virginia, Gunnison, CO 81230, or via MCI Mail 369-4376. Scott also operates a BBS devoted to computer programming, outdoor activities, and science, at 303-641-5125.


In mid-1988, Zortech released its native code C++ compiler for the IBM PC and compatibles. Zortech C++, Version 1.0, was based on the prevailing standard of the time -- AT&T's C++ 1.x. As a bonus, Zortech C++ was one of the first true source-to-object code C++ compilers in existence. This first version was a very good package, and it boosted the use of C++ on MS-DOS computers. It was with this compiler that I began seriously studying and using C++.

Recently, Zortech released Version 2.0 of its C++ compiler. It was a gigantic step forward, being the first implementation of the AT&T 2.0 version of C++ for MS-DOS. At the same time, Zortech released the first source-level debugger for an MS-DOS C++, improved the programming tools, and enhanced the documentation significantly.

I've been using this compiler in my work (book and article writing) as well as my research into fractal geometry and chaos theory. This article discusses how the product has performed in my C++ projects. Along the way, I'll cover the workings of the compiler and its tools, and the way in which it implements the C++ language.

Fractal Geometry

Chaos science, of which fractal geometry is a part, combines mathematics and physics to describe systems which are not understandable by traditional methods. Fractal geometry differs from regular, or Euclidian, geometry in many ways. Euclidian shapes, such as squares and spheres, can be generated and measured using simple formulas. Fractal shapes, however, are created from iterated (and often recursive) algorithms. This is why computers are an integral part of fractal geometry research. In fact, most fractal shapes were not discovered until computers became available for mathematical research.

One of my C++ projects involved writing a program to display a set of fractals similar to the Mandelbrot set. Using a variation of the same recursive algorithm, it is possible to find shapes in the complex plane that closely resemble single-celled life forms. These so-called "biomorphs" can be used to show the dynamics of complex numbers as well as the application of fractal images to modeling microscopic creatures.

In order to develop a C++ program for generating pictures of fractal biomorphs, I needed to have a complex number library. Usually, programs of this type are written in Fortran, which directly supports complex numbers. Fortran, in fact, is very efficient in handling complex number arithmetic. Is it possible to create an efficient complex number class with Zortech C++ 2.0?

The result is shown in Listings One and Two . Listing One (COMPLEX.HPP, page 112) shows the header files for the class Complex, and Listing Two (COMPLEX.CPP, page 114) is the implementation of the non inline methods. To make the class efficient, all 66methods are defined as inline in Listing One, except those methods that do division, power calculations, and stream I/O. The latter are defined in Listing Two.

Inline methods make complex object calculations quicker than if those methods were actual functions. However, my preliminary results showed that the objects were still 50 percent slower than the equivalent Fortran program. The standard compile options make floating-point calculations use a combined coprocessor/emulator library, which, according to my tests, is 10 to 20 percent slower than equivalent floating-point libraries provided with Microsoft's and Borland's C compilers.

I solved that problem by using the -f option when compiling with Zortech C++; that option tells the compiler to generate inline numeric coprocessor instructions. This is both faster and smaller than the standard method of making calls to floating-point emulator routines (which do use the coprocessor, if present). The disadvantage is that a program compiled with -f will run only on a computer that contains or emulates a math coprocessor.

This is a good place to point out that Zortech C++ is a globally-optimizing compiler. A section in the reference manual describes how the optional-optimizer pass works. The compiler has three passes. The first pass generates intermediate code, and the third pass converts intermediate code to native-code form. The optimizer is an optional second pass, which optimizes the intermediate code. This approach is terrific. During development, you can leave out the optimizer pass to get fast compile times. Then, on your final compile, you run the optimizer to generate fast, tight code. My experiments have shown that Zortech C++ 2.0 is competitive with other optimizing compilers such as Microsoft and Watcom.

Integrated Environment

Zortech C++ 2.0 comes with a set of integrated tools. You can edit, compile, and debug your programs in a single environment by using the Zortech Editor (ZED) to control both the compiler and debugger. While this system is not as smooth or quick as the environment provided by other compilers, it offers the advantage of more powerful tools. In a completely integrated environment, such as those that come with Microsoft QuickC and Borland Turbo C, all of the environment's applications are built into one executable file. Thus, you have an editor, debugger, and compiler all in one large .EXE file. This uses a lot of memory and puts restrictions on the power of the individual applications. Many professional programmers disdain integrated environments because of the large memory requirements and limited capabilities of the built-in tools.

Zortech's approach exchanges some speed and flashiness in order to integrate full-featured applications development tools. The ZED is much improved over its predecessor, and supports multiple file buffers, simple macros, and numerous other features expected of an editor by professional programmers. Instead of building a compiler into the editor, ZED simply calls the command-line compiler and traps the error messages. This isn't as fast or slick as a Turbo Pascal, but it does provide you with the power of a real programmer's editor at a minimal cost.

Debugging

The Zortech debugger (ZDB) is also much improved over the previous version. The interface is easier to use, the mouse support works better than you would expect, and C++ is fully supported. In fact, this is the first source-level C++ debugger for MS-DOS. It manages to handle the infamous C++ "name mangling" (a process whereby C++ internally changes the names of functions and objects to differentiate among overloaded names). ZDB allows you to examine and manipulate identif

ZDB is a capable debugger; it uses a set of overlapping windows to show everything from source code to registers to local and global variables and constants. One item I didn't like about the debugger was its tendency to "forget" things. If I set up the local's window to expand certain variables, those expansions would be forgotten by ZDB if I stepped into a function call and back.

Another debugger problem occurred when I was debugging the Complex class. Because inline methods actually do not exist as functions, there is no way to trace them in the debugger. That's fine and logical. To get around this problem, Zortech provided a compiler switch (-c), which explicitly turns all inline methods into actual functions. This would allow the debugging of these methods. Alas, while the compiler correctly generates the inline methods as functions, it fails to create debugging information for those functions. Thus, while the functions exist, the debugger had no information to trace into them. This is a minor bug in the compiler, not the debugger.

This debugger has an amazing number of features, including the ability to provide a profile of program execution. During months of use both the beta and release versions of the debugger were able to handle everything not mentioned before. An additional bonus is that ZDB is CodeView compatible, and it can debug C and assembly language programs alongside C++.

Is It "Real" C++?

When building the Complex class, I realized the need to have input and output routines for Complex objects. The obvious approach (and the one I used) is to create functions that use the << and >> operators and the streams class. Zortech supports an enhanced version of the original streams classes as defined in Stroustrup's book, The C++ Programming Language. AT&T has since enhanced this library itself, but Zortech does not yet support the new AT&T additions (called "iostreams"), saying that AT&T has not made a complete specification available.

The Biomorph program shown in Listing Three, page 119, required a graphics module. Rather than use the Flash Graphics library included with Zortech C++, I linked in the low-level routines from my own C and C++ graphics library. Because of space considerations, the graphics library source code (zg_lwlvl.h and zg_lwlvl.c) is not included with this article. However, the code is available through the DDJ Forum of CompuServe, through the DDJ Listing Service, through my BBS, or direc

To link these C-language functions with a C++ program required the use of type-safe linkage, one of the more important additions AT&T made to C++ 2.0. Type-safe linkage allows object modules from other languages to be linked with C++ safely. By declaring the native language of a module, the C++ linker knows how names are to be resolved. This lets C++ do whatever it wants with the names in C++ modules, while allowing other languages to do their own thing. In addition to the C and C++ types of linkage, Zortech C++ supports Pascal-style linkage. This is a big plus for those people writing C++ programs for OS/2 and Microsoft Windows.

Did I say Windows? Yes, indeed, Zortech C++ supports Microsoft Windows programming. Zortech does not provide a library of classes or interface routines related to Windows programming. However, the compiler is capable of being used with the Microsoft Windows Software Development Kit (SDK), and documentation is provided that explains how to use the Zortech compiler with the SDK. Because Microsoft C is not too strict about prototypes and other things, from an ANSI standpoint most Windows programs are sloppily written. Zortech is stricter, and you may have to make minor changes to get Windows programs to compile. However, I was able to take several Windows applications, including some from the SDK, and make them compile with Zortech C and C++. While the OS/2 compiler is an extra cost option, the addition of Microsoft Windows compatibility to the MS-DOS compiler will provide the first true C++ compiler for Windows developers.

AT&T Compatibility

Zortech C++ 2.0 is close to the AT&T standard. Zortech does not support pointers to members, the obscure asm( )pseudo-function, or fine-grained overload resolution. All of this is documented in the READ.ME file, along with a list of known bugs in the compiler. If only other companies were so honest; most manufacturers seem to make an art of trying to hide the bugs in their products. All compilers have bugs; Zortech is the only one willing to admit to them so they don't sneak up on you. As it is, the bugs in Zortech C++ are relatively trivial, and workarounds are provided when possible.

Zortech C++ does support multiple inheritance, virtual and abstract base classes, anonymous unions, scope resolution, and just about everything else you'd care to throw at it from the AT&T 2.0 Reference. I've put some pretty messy code through the Zortech compiler, and it hasn't even blinked.

It is interesting to note that cfront, AT&T's own C++, is not entirely compatible with the language as described in the AT&T C++ 2.0 Reference Manual. For instance, cfront does not support the volatile keyword, which Zortech does. While the Zortech product is not a perfect implementation of the language described in the AT&T standard, it's close. Zortech people have said they will be adding the missing 2.0 features in a subsequent release.

The Goodies

For those of us who have used the Zortech 1.x product, documentation has always been a sore point. To put it bluntly, the documentation with Zortech C++ 1.0 was terrible. That isn't true of the documents that come with Zortech C++ 2.0: These manuals, aside from minor spelling errors, are simply great. The C++ Compiler Reference is well-organized and clearly written. It contains a good, if short, C++ tutorial, along with lots of "extra" information on subjects such as the global optimizer and the proper use of C++. The C++ Function Reference, C++ Tools, and C++ Debugger manuals are complete and thorough, including many examples.

In the past, Zortech C++ has been criticized for not being as "fancy" as other compilers. Borland and Microsoft include a number of functions in their C libraries that Zortech did not support. That complaint can now be dropped; Zortech has added a number of functions to its library. Not only have a number of functions been added to increase compatibility with the Microsoft C library, there are new functions to handle expanded memory and TSRs. Library functions carried over from the previous version support graphics, direct-video displays, mouse routines, interrupt trapping, and the PC speaker.

The compiler has been extended to support a new pointer type, known as a "handle." A handle pointer can be used to access virtual or expanded memory, a major plus for memory-hungry applications. If you don't want to use a language extension, a library of expanded memory functions is also included.

The C++ Tools package is included with the Developer's Edition of the compiler, and is also available separately. Unlike Zortech's, most C++ products fail to provide a significant library of classes. C++ Tools has classes for various kinds of lists, dynamic and virtual arrays, binary trees, hashed tables, BCD math, time and date handling, interrupt handlers, string and text editing, event queues, windows, and money. While some of the classes are not implemented as well as they could be, this is an excellent starting point for those people who do not want to spend time developing their own basic classes.

A "disk 13 of 12" contains a number of other goodies. These include a C++ overlay for the graphics library, an example C++ application, and a library of interesting C routines. While the C++ graphics library is weak, the Doodle program is a good example of C++ program design.

Back to the Biomorphs

The Biomorph program works great. Zortech C++ compiles the program so that it runs at the same speed as the Fortran version. If you have any interest at all in the new science of chaos, or just like to generate interesting pictures on your PC, this program is a great opportunity to experiment. One word of warning: The program performs millions of floating-point operations to generate an image. A PC without a math coprocessor will run Biomorph very slowly; the program is totally unusable on an 8088/86-based PC. It works well on my 20MHz 80386 with 16-bit VGA. Take heart, though -- the real researchers on this subject use supercomputers to generate this stuff.

Summing Up

The Zortech C++ compiler has become an intimate part of the work I do. From articles and books on C++ to personal research projects and consulting work, Zortech C++ has had more mileage put on it in the last year than any other compiler in the house. The new 2.0 compiler has improved the product from good to excellent. While it's not perfect, Zortech C++ is one of the best MS-DOS products I've had the luck to use. If you want to work with C++ 2.0 on an MS-DOS computer, I can highly recommend the Zortech 2.0 release.

Product Information

Zortech C++ V2.0 Zortech, Inc. 1165 Massachusetts Ave. Arlington, MA 02174 800-848-8408

Developer's Edition (includes compiler, debugger, tools, and library source) $450 C++ compiler $199.95 C++ debugger $149.95 C++ tools $149.95 OS/2 compiler upgrade $149.95

Currently, Version 2.0 of the Zortech C++ compiler (by itself) costs $200, and the Developer's Edition (including library source code, a C++ class library, and the debugger) costs $450. Considering what this package offers, I'd opt for the more complete Developer's Edition.

Bibliography

Stroustrup, B. The C++ Programming Language, Addison-Wesley, Reading, Mass.: 1985.

_EXAMINING ZORTECH C++ 2.0_ by Scott Robert Ladd

[LISTING ONE]

<a name="0043_000f">

//  Header:     Complex
//  Version:    2.00    28-Oct-1989
//  Language:   C++ 2.0;  Environ:  Any;  Compilers: Zortech C++ 2.01
//  Purpose:    Provides the class "Complex" for C++ programs. The majority
//              of the class is implemented inline for efficiency. Only
//              the division, power, and i/o methods are actual functions.
//  Written by: Scott Robert Ladd, 705 West Virginia, Gunnison CO 81230
//              BBS (303)641-6438; FidoNet 1:104/708

#if !defined(__COMPLEX_HPP)
#define __COMPLEX_HPP 1
#include "stream.hpp"
#include "math.h"

class Complex
    {
    private:
        double Real;   // Real part
        double Imag;   // Imaginary part
        static void (* ErrorHandler)();
    public:
        // constructors
        Complex (void);
        Complex (const Complex & C);
        Complex (double & R, double & I);
        Complex (double & R);
        // method to set error handler function
        static void SetErrorHandler(void (* UserHandler)());
        // value extraction methods
        friend double real(const Complex & C);
        friend double imag(const Complex & C);
        // assignment methods
        void operator = (const Complex & C);
        void operator = (double & R);
        // unary minus method
        Complex operator - ();
        // calculation methods
        friend Complex operator + (const Complex & C1, const Complex &C2);
        friend Complex operator - (const Complex & C1, const Complex &C2);
        friend Complex operator * (const Complex & C1, const Complex &C2);
        friend Complex operator / (const Complex & C1, const Complex &C2);

        Complex operator += (const Complex & C);
        Complex operator -= (const Complex & C);
        Complex operator *= (const Complex & C);
        Complex operator /= (const Complex & C);
        // comparison methods
        friend int operator == (const Complex & C1, const Complex & C2);
        friend int operator != (const Complex & C1, const Complex & C2);
        friend int operator <  (const Complex & C1, const Complex & C2);
        friend int operator <= (const Complex & C1, const Complex & C2);
        friend int operator >  (const Complex & C1, const Complex & C2);
        friend int operator >= (const Complex & C1, const Complex & C2);
        // utility methods
        friend double abs(const Complex & C);
        friend double norm(const Complex & C);
        friend double arg(const Complex & C);
        // polar coordinate methods
        friend Complex polar(double Radius, double Theta = 0.0);
        friend Complex conj(const Complex & C);
        // trigonometric methods
        friend Complex cos(const Complex & C);
        friend Complex sin(const Complex & C);
        friend Complex tan(const Complex & C);
        friend Complex cosh(const Complex & C);
        friend Complex sinh(const Complex & C);
        friend Complex tanh(const Complex & C);
        // logarithmic methods
        friend Complex exp(const Complex & C);
        friend Complex log(const Complex & C);
        // "power" methods
        friend Complex pow(const Complex & C, const Complex & Power);
        friend Complex sqrt(const Complex & C);
        // output method
        friend ostream & operator << (ostream & Output, const Complex & C);
        friend istream & operator >> (istream & Input,  Complex & C);
    };
// constructors
inline Complex::Complex (void)
    {
    Real = 0.0;
    Imag = 0.0;
    }
inline Complex::Complex (const Complex & C)
    {
    Real = C.Real;
    Imag = C.Imag;
    }
inline Complex::Complex (double & R, double & I)
    {
    Real = R;
    Imag = I;
    }
inline Complex::Complex (double & R)
    {
    Real = R;
    Imag = 0.0;
    }
inline void Complex::SetErrorHandler(void (* UserHandler)())
    {
    ErrorHandler = UserHandler;
    }
// value extraction methods
inline double real (const Complex & C)
    {
    return C.Real;
    }
inline double imag (const Complex & C)
    {
    return C.Imag;
    }

// assignment method
inline void Complex::operator = (const Complex & C)
    {
    Real = C.Real;
    Imag = C.Imag;
    }
inline void Complex::operator = (double & R)
    {
    Real = R;
    Imag = 0.0;
    }
// unary minus method
inline Complex Complex::operator - ()
    {
    Complex Result;
    Result.Real = -Real;
    Result.Imag = -Imag;
    return Result;
    }
// calculation methods
inline Complex operator + (const Complex & C1, const Complex &C2)
    {
    Complex Result;
    Result.Real = C1.Real + C2.Real;
    Result.Imag = C1.Imag + C2.Imag;
    return Result;
    }
inline Complex operator - (const Complex & C1, const Complex &C2)
    {
    Complex Result;
    Result.Real = C1.Real - C2.Real;
    Result.Imag = C1.Imag - C2.Imag;
    return Result;
    }
inline Complex operator * (const Complex & C1, const Complex &C2)
    {
    Complex Result;
    Result.Real = (C1.Real * C2.Real) - (C1.Imag * C2.Imag);
    Result.Imag = (C1.Real * C2.Imag) + (C1.Imag * C2.Real);
    return Result;
    }
inline Complex Complex::operator += (const Complex &C)
    {
    Real += C.Real;
    Imag += C.Imag;
    return *this;
    }
inline Complex Complex::operator -= (const Complex &C)
    {
    Real -= C.Real;
    Imag -= C.Imag;
    return *this;
    }
inline Complex Complex::operator *= (const Complex &C)
    {
    double OldReal;
    OldReal = Real; // save old Real value
    Real = (Real * C.Real) - (Imag * C.Imag);
    Imag = (OldReal * C.Imag) + (Imag * C.Real);
    return *this;
    }
// comparison methods
inline int operator == (const Complex & C1, const Complex & C2)
    {
    return (C1.Real == C2.Real) && (C1.Imag == C2.Imag);
    }
inline int operator != (const Complex & C1, const Complex & C2)
    {
    return (C1.Real != C2.Real) || (C1.Imag != C2.Imag);
    }
inline int operator <  (const Complex & C1, const Complex & C2)
    {
    return abs(C1) < abs(C2);
    }
inline int operator <= (const Complex & C1, const Complex & C2)
    {
    return abs(C1) <= abs(C2);
    }
inline int operator >  (const Complex & C1, const Complex & C2)
    {
    return abs(C1) > abs(C2);
    }
inline int operator >= (const Complex & C1, const Complex & C2)
    {
    return abs(C1) >= abs(C2);
    }
// utility methods
inline double abs(const Complex & C)
    {
    double Result;
    Result = sqrt(C.Real * C.Real + C.Imag * C.Imag);
    return Result;
    }
inline double norm(const Complex & C)
    {
    double Result;
    Result = (C.Real * C.Real) + (C.Imag * C.Imag);
    return Result;
    }
inline double arg(const Complex & C)
    {
    double Result;
    Result = atan2(C.Imag, C.Real);
    return Result;
    }
// polar coordinate methods
inline Complex polar(double Radius, double Theta)
    {
    Complex Result;
    Result.Real = Radius * cos(Theta);
    Result.Imag = Radius * sin(Theta);
    return Result;
    }
inline Complex conj(const Complex & C)
    {
    Complex Result;
    Result.Real =  C.Real;
    Result.Imag = -C.Imag;
    return Result;
    }
// trigonometric methods
inline Complex cos(const Complex & C)
    {
    Complex Result;
    Result.Real =  cos(C.Real) * cosh(C.Imag);
    Result.Imag = -sin(C.Real) * sinh(C.Imag);
    return Result;
    }
inline Complex sin(const Complex & C)
    {
    Complex Result;
    Result.Real = sin(C.Real) * cosh(C.Imag);
    Result.Imag = cos(C.Real) * sinh(C.Imag);
    return Result;
    }
inline Complex tan(const Complex & C)
    {
    Complex Result;
    Result = sin(C) / cos(C);
    return Result;
    }
inline Complex cosh(const Complex & C)
    {
    Complex Result;
    Result = cos(C.Imag) * cosh(C.Real);
    Result = sin(C.Imag) * sinh(C.Real);
    return Result;
    }
inline Complex sinh(const Complex & C)
    {
    Complex Result;
    Result.Real = cos(C.Imag) * sinh(C.Real);
    Result.Imag = sin(C.Imag) * cosh(C.Real);
    return Result;
    }
inline Complex tanh(const Complex & C)
    {
    Complex Result;
    Result = sinh(C) / cosh(C);
    return Result;
    }
// logarithmic methods
inline Complex exp(const Complex & C)
    {
    Complex Result;
    double X = exp(C.Real);
    Result.Real = X * cos(C.Imag);
    Result.Imag = X * sin(C.Imag);
    return Result;
    }
inline Complex log(const Complex & C)
    {
    Complex Result;
    double Hypot = abs(C);
    if (Hypot > 0.0)
        {
        Result.Real = log(Hypot);
        Result.Imag = atan2(C.Imag, C.Real);
        }
    else
        Complex::ErrorHandler();
    return Result;
    }
#endif // __Complex_HPP




<a name="0043_0010"><a name="0043_0010">
<a name="0043_0011">
[LISTING TWO]
<a name="0043_0011">

//  Module:     Complex
//  Version:    2.00    28-Oct-1989
//  Language:   C++ 2.0;  Environ:    Any;  Compilers:  Zortech C++ 2.01
//  Purpose:    Provides the class "Complex" for C++ programs. The majority
//              of the class is implemented inline for efficiency. Only
//              the division, power, and i/o methods are actual functions.
//  Written by: Scott Robert Ladd, 705 West Virginia, Gunnison CO 81230
//              BBS (303)641-6438; FidoNet 1:104/708

#include "math.h"
#include "stdlib.h"
#include "Complex.hpp"
#include "stream.hpp"

static void DefaultHandler();
void (* Complex::ErrorHandler)() = DefaultHandler;
static void DefaultHandler()
    {
    cout << "\aERROR in complex object: DIVIDE BY ZERO\n";
    exit(1);
    }
// division methods
Complex operator / (const Complex & C1, const Complex & C2)
    {
    Complex Result;
    double Den;
    Den = norm(C2);
    if (Den != 0.0)
        {
        Result.Real = (C1.Real * C2.Real + C1.Imag * C2.Imag) / Den;
        Result.Imag = (C1.Imag * C2.Real - C1.Real * C2.Imag) / Den;
        }
    else
        Complex::ErrorHandler();
    return Result;
    }
Complex Complex::operator /= (const Complex & C)
    {
    double Den, OldReal;
    Den = norm(C);
    if (Den != 0.0)
        {
        OldReal = Real;
        Real = (Real * C.Real + Imag * C.Imag) / Den;
        Imag = (Imag * C.Real - OldReal * C.Imag) / Den;
        }
    else
        Complex::ErrorHandler();
    return *this;
    }
// "power" methods
Complex pow(const Complex & C, const Complex & Power)
    {
    Complex Result;
    if (Power.Real == 0.0 && Power.Imag == 0.0)
        {
        Result.Real = 1.0;
        Result.Imag = 0.0;
        }
    else
        {
        if (C.Real != 0.0 || C.Imag != 0.0)
            Result = exp(log(C) * Power);
        else
            Complex::ErrorHandler();
        }
    return Result;
    }
Complex sqrt(const Complex & C)
    {
    Complex Result;
    double r, i, ratio, w;
    if (C.Real != 0.0 || C.Imag != 0.0)
        {
        r = C.Real < 0.0 ? -C.Real : C.Real;
        i = C.Imag < 0.0 ? -C.Imag : C.Imag;
        if (r > i)
            {
            ratio = i / r;
            w = sqrt(r) * sqrt(0.5 * (1.0 + sqrt(1.0 + ratio * ratio)));
            }
        else
            {
            ratio = r / i;
            w = sqrt(i) * sqrt(0.5 * (ratio + sqrt(1.0 + ratio * ratio)));
            }
        if (C.Real > 0)
            {
            Result.Real = w;
            Result.Imag = C.Imag / (2.0 * w);
            }
        else
            {
            Result.Imag = (C.Imag > 0.0) ? w : - w;
            Result.Real = C.Imag / (2.0 * Result.Imag);
            }
        }
    else
        Complex::ErrorHandler();
    return Result;
    }
// output method
ostream & operator << (ostream & Output, const Complex & C)
    {
    Output << form("(%+1g%+1gi)",C.Real,C.Imag);
    return Output;
    }
istream & operator >> (istream & Input, Complex & C)
    {
    char Ch;
    C.Real = 0.0;
    C.Imag = 0.0;
    Input >> Ch;
    if (Ch == '(')
        {
        Input >> C.Real >> Ch;
        if (Ch == ',')
            Input >> C.Imag >> Ch;
        if (Ch != ')')
            Input.clear(_bad);
        }
    else
        {
        Input.putback(Ch);
        Input >> C.Real;
        }
    return Input;
    }




<a name="0043_0012"><a name="0043_0012">
<a name="0043_0013">
[LISTING THREE]
<a name="0043_0013">

//  Program:    Biomorph    (Generate non-standard fractals)
//  Version:    1.01        31-Oct-1989
//  Language:   C++ 2.0;  Environ:  Any;  Compilers:  Zortech C++ 2.01
//  Purpose:    Generates fractals based on complex number formula iterations
//  Written by: Scott Robert Ladd, 705 West Virginia, Gunnison CO 81230
//              BBS (303)641-6438;  FidoNet 1:104/708

#include "conio.h"
#include "zg_lwlvl.h"
#include "complex.hpp"

Complex C, Z, Power;
double Range, Xinc, Yinc, Xmax, Ymax, Xorig, Yorig;
int X, Y, I, Iterations, Species;

void GetParams();
int  main();

void GetParams()
    {
    cout << "Biomorph 1.01 -- a complex-plane fractal generator\n";
    cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";

    cout << "This program generates these species of biomorphs...\n";
    cout << "  Species 0: Z^X + C         Species 1: sin(Z) + exp(Z) + C\n";
    cout << "  Species 2: Z^Z + Z^X + C   Species 3: sin(Z) + Z^X + C\n\n";
    do  {
        cout << "What species of biomorph do you want (0..3)? ";
        cin  >> Species;
        }
    while ((Species < 0) || (Species > 3));
    cout << "\nNow we need one or two complex numbers. These can be entered\n";
    cout << "in the following formats (where 'f' indicates a floating-point\n";
    cout << "value:\n";
    cout << "   f -or- (f) (just a real number)\n";
    cout << "   (f,f)      (entering both the real and imaginary parts)\n\n";
    if (Species != 1)
        {
        cout << "Enter the complex power applied to Z: ";
        cin  >> Power;
        }
    cout << "Enter the complex constant C: ";
    cin  >> C;
    cout << "\nNext two numbers are floating point values representing the\n";
    cout << "origin point on the complex plane of the area being viewed.\n\n";
    cout << "Enter the X location of the center of the picture: ";
    cin  >> Xorig;
    cout << "Enter the Y location of the center of the picture: ";
    cin  >> Yorig;
    cout << "\nNext number represents the distance the graph extends away\n";
    cout << "from the above origin.\n\n";
    cout << "Enter the range of the graph: ";
    cin  >> Range;
    cout << "\nFinally, how many iterations should the program perform? ";
    cin  >> Iterations;
    }

int main()
    {
    GetParams();
    if (ZG_Init()) return 1;
    if (ZG_SetMode(ZG_MOD_BESTRES)) return 2;
    Ymax  = ZG_VideoInfo.Ylength;
    Xmax  = Ymax / (1.33333333 * Ymax / ZG_VideoInfo.Xwidth);
    Xinc  = 2.0 * Range / Xmax;
    Yinc  = 2.0 * Range / Ymax;
    Range = -Range;
    for (X = 0; X < Xmax; ++X)
        {
        for (Y = 0; Y < Ymax; ++Y)
            {
            Z = Complex((Range + Xinc * X + Xorig),(Range + Yinc * Y + Yorig));
            for (I = 0; I < Iterations; ++I)
                {
                switch (Species)
                    {
                    case 0 :
                        Z = pow(Z,Power) + C;
                        break;
                    case 1:
                        Z = sin(Z) + exp(Z) + C;
                        break;
                    case 2:
                        Z = pow(Z,Z) + pow(Z,Power) + C;
                        break;
                    case 3:
                        Z = sin(Z) + pow(Z,Power) + C;
                        break;
                    }
                if ((abs(real(Z)) >= 10.0) || (abs(imag(Z)) >= 10.0)
                || (norm(Z) >= 100.0))
                    break;
                }
            if ((abs(real(Z)) < 10.0) ||  (abs(imag(Z)) < 10.0))
                ZG_PlotPixel(X,Y,0);
            else
                ZG_PlotPixel(X,Y,ZG_VideoInfo.NoColors - 1);
            if (kbhit()) break;
            }
        if (kbhit()) break;
        }
    while (!kbhit()) ;
    if (!getch()) getch();
    ZG_Done();
    return 0;
    }











Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.