• Print

Author Topic: Complex number library for the QB64  (Read 272 times)

anttiylikoski

  • Newbie
  • *
  • Posts: 34
    • Email
Complex number library for the QB64
« on: March 04, 2013, 12:46:21 PM »
Could someone point me to the documentation how to write object oriented libraries for the QB64 system.

I have in my hands a complex number library written in the Java language.  I was suggested that I could port it to the QB64 system.

I wrote this Java complex library for work concerning Fourier transforms.

I'm a newbie with the QB64 but not a newbie with computer science.

Andy

DSMan195276

  • Hero Member
  • *****
  • Posts: 1996
  • Yes
    • Email
Re: Complex number library for the QB64
« Reply #1 on: March 04, 2013, 02:16:01 PM »
QB64 does not support (currently) any type of object orientation natively. That in mind, just because it doesn't support object orientation natively doesn't mean it's not possible to emulate that type of functionality in the language. Like people have done in C, you can make object orientated systems in QB64 if you really want, however you will run into the problem that QB64 currently does not have any method of getting and using function pointers (It's currently being considered but isn't a focus right now).

You can however contact with external libraries via DECLARE LIBRARY, and/or write out some C++ code to directly do your work for you. So, if you just write out a few quick functions to handle the function pointers, or even better yet write out some C++ that will handle the objects and then interface with them via DECLARE LIBRARY. It's not amazingly pretty however, and honestly nobody has really had a need to do this yet (Simply because are large majority of the people here are just QB64 coders, and/or just don't know C++ and don't really have any need for object orientation).

If you've ever written libraries for C, I find that the same ideas for those apply well to QB64. Namely, since all functions are in the global space, precede everything relating to your library (Function names, global variables, consts, TYPE's, etc.) with a simple keyword (Such as 'CPLX_' or something similar). That just makes conflicting names very unlikely. As well, when implementing your object structures, just implement them as simple TYPE's (If you take a look at _MEM I think you'll find that to be useful in making your structures). The functions that were inside the objects can just be outside and have an extra parameter being the TYPE.

The last thing you should note, QB64 can't currently create libraries in the traditional sense. You can use external libraries (Such as DLL's or SO's) with QB64, but QB64 can't create DLL or SO files. When making a library for QB64 it's normally just completely done in QB64 code and then '$include:'ed into the program (And it's pretty common to have both a top and bottom file to include). Because of this I'm not entirely sure that QB64 is what you're wanting. If you're looking to make a DLL/SO library for use with other languages, programs, etc. you may be better off just doing it in a language like C++ (And since C++ has native object-orientation it shouldn't be to hard to implement).

Matt
"Cast your cares on the Lord and he will sustain you; he will never let the righteous be shaken" -- Psalm 55:22
QB64 Linux Installer

anttiylikoski

  • Newbie
  • *
  • Posts: 34
    • Email
Re: Complex number library for the QB64
« Reply #2 on: March 04, 2013, 06:51:38 PM »
I attempted to simulate the Java capability

Code: [Select]

Complex CXNUM = new Complex();


with the following QB64 code:

Code: [Select]

' Complex library by Antti J Ylikoski
'
' 2013-03-05
'


TYPE CX
    RE AS DOUBLE ' real part
    IM AS DOUBLE ' imaginary part
    MD AS DOUBLE ' modulus (length of vector)
    AR AS DOUBLE ' argument (angle of vector)
END TYPE


DIM CXNUM AS CX

CXNUM = CX_NEW

END



FUNCTION CX_NEW ()
CX_NEW = _MEMNEW(LEN(CX))
END FUNCTION



but this causes a type mismatch problem.

Any ideas how to overcome this difficulty?

Andy

DSMan195276

  • Hero Member
  • *****
  • Posts: 1996
  • Yes
    • Email
Re: Complex number library for the QB64
« Reply #3 on: March 04, 2013, 07:13:38 PM »
Ah, you're a bit confused. Java to QB64 is somewhat of a big leap because their pretty far apart from each-other language wise (In fact, they have just about nothing in common). Some quick things you'll need to keep in mind:

1. QB64 has no objects, and thus no reference types (There is _MEM, but it's a bit different then a reference type, I'll get to that lower)
2. Everything is pass by reference (Because of this you can pass back values in the parameters of SUB's/FUNCTION's)
3. Functions can't return user-defined TYPE's or _MEM's. They can return numbers or strings. This functionality is planned but not there yet. This isn't as big of a deal as it seems though, because you can just pas the variable of that TYPE in the parameters and since it's pass by reference it will be modified.
4. QB64 is not strict on declaring variables -- Variables will be automatically created when needed if it doesn't already exist. -- The default type is SINGLE so if you need a variable of a different type either use a type suffix or use DIM to declare it as that type.
5. _MEM is used for creating and referencing pieces of memory. It's pretty much a direct counterpart of malloc in C programming. However, you have no need for it in this context, and I'll explain why. You should keep in mind though if you do use _MEM's, QB64 has no garbage collection, so make sure to clear _MEM's via _MEMFREE when you're done with them (Java doesn't force you to do this).

When declaring a variable, you have no reason to manually allocate the memory (Or do a 'new' statement) because TYPE's are just plain variables (There is no automatic constructor run, remember they aren't objects). However it's easy to have a 'CX_NEW()' sub that you can call to initalize a CX (I don't think you'll need it though, and I'll explain why in a second). This is what you're code would need to be to work like what you want:

Code: [Select]
' Complex library by Antti J Ylikoski
'
' 2013-03-05
'


TYPE CX
    RE AS DOUBLE ' real part
    IM AS DOUBLE ' imaginary part
    MD AS DOUBLE ' modulus (length of vector)
    AR AS DOUBLE ' argument (angle of vector)
END TYPE


DIM CXNUM AS CX
CX_NEW CXNUM
END

SUB CX_NEW (C AS CX)

END SUB

You can probably tell why CX_NEW isn't needed in this context with that code. Remember, Once you run the line 'DIM CXNUM AS CX' we're not making a reference like with Java but creating the variable (You can think of TYPE's the same as primitives in Java). So at that point, CXNUM already exists and is usable with no other setup required. More noticeable is that no memory allocated is needed (Just like primitives). So unless you need to set the values inside the TYPE to something other then zero then you have no reason to even need a constructor because it won't do anything.

It's probably still a good idea to use CX_NEW as a constructor SUB just in case you decide at some point you don't want zero's as the defaults (And if you already have the code in there then you don't need to track down each CX to add a call to CX_NEW).

As you may have noticed I do know some Java (I can't say I'm a fan of Java, but to each his own). If you can show me some basic Java stuff you're hoping to convert over I could explain some basics of how you may go about doing it. I think after taking the above into account you'll be ok though. Each function that was in the object should just become a 'CX_' function which will probably take a CX as it's first parameter (And
remember since it's pass by value, lots of your functions that retuned values can actually just become SUB's and modify the parameters directly).

Hope that helps,
Matt
"Cast your cares on the Lord and he will sustain you; he will never let the righteous be shaken" -- Psalm 55:22
QB64 Linux Installer

anttiylikoski

  • Newbie
  • *
  • Posts: 34
    • Email
Re: Complex number library for the QB64
« Reply #4 on: March 04, 2013, 09:27:18 PM »
OK, thanks very much indeed.

Following comes a sample from my Java library:

Code: [Select]

/*
 * The Java class for complex numbers and complex arithmetic.
 *
 * AJY 2013-02-10
 */

import java.lang.Math;
import java.util.Random;

public class Complex
{
    public double re, im;   // for rectang representation
    public double mod, arg; // for polar representation

    private static Random rnd = new Random();

    Complex() // no-argument constructor
    {
this(0.0, 0.0);
mod = 0.0;
arg = 0.0;
    }

    Complex(double r, double i) // constructor with args
    {
re = r;
im = i;

mod = 0.0; // Default is the rectang representation
arg = 0.0;
    }

    // arithmetic

    public Complex plus(Complex x)
    {
return new Complex(re + x.re, im + x.im);
    }

    public Complex minus(Complex x)
    {
return new Complex(re - x.re, im - x.im);
    }

    public Complex times(Complex x)
    {
return new Complex(re * x.re - im * x.im,
   im * x.re + re * x.im);
    }

    public Complex div(Complex x)
    {
return new Complex((re*x.re + im*x.im)/(x.re*x.re + x.im*x.im),
   (im*x.re - re*x.im)/(x.re*x.re + x.im*x.im));
    }


    public Complex realMul(double r) // multiply by a real number
    {
return new Complex(r*re, r*im);
    }

    public Complex reciprocal() // inverse, reciprocal
    {
return this.conjugate().realMul(1.0/(re*re + im*im));
    }

    public Complex conjugate() // complex conjugate
    {
return new Complex(re, -im);
    }

} // end demo of class Complex



regards, Andy

I'm not a big lover of Java either.  I think it is like the ancient PL/I: very big, and one can do everything conceivable with it.



DSMan195276

  • Hero Member
  • *****
  • Posts: 1996
  • Yes
    • Email
Re: Complex number library for the QB64
« Reply #5 on: March 04, 2013, 09:46:42 PM »
Thanks for that. here's the basics of how to set it up in QB64:

Code: [Select]
TYPE CX_Complex
  re as DOUBLE
  im as DOUBLE
  md as DOUBLE ' "mod" is a keyword so you can't use that -- I think you already figured that out though
  arg as DOUBLE
END TYPE

DIM c1 as CX_Complex, c2 as CX_Complex, result as CX_Complex

c1.re = 1 'Setting some values
c1.im = 2

c2.re = 3
c2.im = 10

CX_plus c1, c2, result

PRINT "Result:"
print result.re; result.im


SUB CX_plus(c1 as CX_Complex, c2 as CX_Complex, result as CX_Complex)
result.re = c1.re + c2.re
result.im = c1.im + c2.im
END SUB

SUB CX_RealMul(c1 as CX_Complex, v as DOUBLE, result as CX_Complex)
result.re = c1.re * v
result.im = c1.im * v
END SUB

SUB CX_reciprocal(c1 as CX_Complex, result as CX_Complex)
DIM conj as CX_Complex
CX_Conjugate c1, conj
CX_realMul conj, 1.0 / (c1.re * c1.re + c1.im * c1.im), result
END SUB

'Conj is the resultant conjugate
SUB CX_Conjugate(c1 as CX_Complex, conj as CX_Complex)
conj.re = c1.re
conj.im = -c1.im
END SUB


Since you can think of CX_Complex like a primitive, it doesn't need any initialization and the default values are just fine (Just sets everything to zero) so in this case no constructor type sub is needed. Also, functions that returned a 'Complex' object are all SUB's that have a third 'result' parameter. Since it's all pass by reference, instead of returning a 'Complex' it can just modify one sent as the last parameter.

I implemented the reciprocal function you had because it seemed like it'd be the best example (Since it's the most complicated and involves lots of object calls). It pretty much just comes down to, instead of calling the function via a '.' from the object, you call the function and send it the objects you plan on modifying. The functions aren't part of the CX_Complex type but instead act on CX_Complex type.

Feel free to ask if you have any questions, implementing the rest of the CX_ functions should pretty much look almost identical to the ones I wrote out, more or less.

Matt
"Cast your cares on the Lord and he will sustain you; he will never let the righteous be shaken" -- Psalm 55:22
QB64 Linux Installer

anttiylikoski

  • Newbie
  • *
  • Posts: 34
    • Email
Re: Complex number library for the QB64
« Reply #6 on: March 05, 2013, 05:27:43 AM »
Here comes my translation from Java to QB64:

Code: [Select]


' QB64 complex library
' Antti J Ylikoski and Matt "DSMan195276" 2013-03-05
'
' Define the type CX_Complex and operators whose result always is
' the last argument
'
' Very very straightforward translation from Java
' Originally written to carry out Fourier transforms in Java
'


TYPE CX_Complex
    re AS DOUBLE ' real part
    im AS DOUBLE ' imaginary part
    md AS DOUBLE ' modulus (polar rep)
    ar AS DOUBLE ' argument (polar rep)
END TYPE



'sum
SUB CX_plus (c1 AS CX_Complex, c2 AS CX_Complex, result AS CX_Complex)
result.re = c1.re + c2.re
result.im = c1.im + c2.im
END SUB

'difference
SUB CX_minus (c1 AS CX_Complex, c2 AS CX_Complex, result AS CX_Complex)
result.re = c1.re - c2.re
result.im = c1.im - c2.im
END SUB

' product
SUB CX_times (c1 AS CX_Complex, c2 AS CX_Complex, result AS CX_Complex)
result.re = c1.re * c2.re - c1.im * c2.im
result.im = c1.im * c2.re + c1.re * c2.im
END SUB

' division, quotient
SUB CX_div (c1 AS CX_Complex, c2 AS CX_Complex, result AS CX_Complex)
result.re = (c1.re * c2.re + c1.im * c2.im) / (c2.re ^ 2 + c2.im ^ 2)
result.im = (c1.im * c2.re - c1.re * c2.im) / (c2.re ^ 2 + c2.im ^ 2)
END SUB

' multiply by a real number
SUB CX_RealMul (c1 AS CX_Complex, v AS DOUBLE, result AS CX_Complex)
result.re = c1.re * v
result.im = c1.im * v
END SUB

' inverse, reciprocal
SUB CX_reciprocal (c1 AS CX_Complex, result AS CX_Complex)
DIM conj AS CX_Complex
CX_Conjugate c1, conj
CX_RealMul conj, 1.0 / (c1.re * c1.re + c1.im * c1.im), result
END SUB

'Conj is the complex conjugate
SUB CX_Conjugate (c1 AS CX_Complex, conj AS CX_Complex)
conj.re = c1.re
conj.im = -c1.im
END SUB

' square of a complex number
SUB CX_Square (c1 AS CX_Complex, result AS CX_Complex)
CX_times c1, c1, result
END SUB

' convert the representation to polar (rect is the default)
SUB CX_ConvPolar (c1 AS CX_Complex)
c1.md = SQR(c1.re ^ 2 + c1.im ^ 2)
c1.ar = ATN(c1.im / c1.re)
END SUB

' convert representation to rectangular
SUB CX_ConvRect (c1 AS CX_Complex)
c1.re = c1.md * COS(c1.ar)
c1.im = c1.md * SIN(c1.ar)
END SUB

' Some nontrivial functions of complex numbers.
' For the formulae see e. g. the Wikipedia.

' e to complex power
' exp(x + yj) = exp(x) * exp(yj)
'             = exp(x) * (cos(y) + j*sin(y))
SUB CX_exp (c1 AS CX_Complex, result AS CX_Complex)
DIM aux AS CX_Complex
aux.re = COS(c1.im) ' cos(y)
aux.im = SIN(c1.im) ' j*sin(y)
CX_RealMul aux, EXP(c1.re), result
END SUB

' natural logarithm of a complex number
SUB CX_ln (c1 AS CX_Complex, result AS CX_Complex)
DIM aux AS CX_Complex
aux.re = LOG(ABS(modulus(c1)))
aux.im = argument(c1)
result = aux
END SUB

' the modulus of a complex number
FUNCTION modulus (c1 AS CX_Complex)
CX_ConvPolar c1
modulus = c1.md
END FUNCTION

' the argument of a complex number
FUNCTION argument (c1 AS CX_Complex)
CX_ConvPolar c1
argument = c1.ar
END FUNCTION

' raise a complex number to an integer power
' the deMoivre formula
SUB CX_powerInt (c1 AS CX_Complex, n AS INTEGER, result AS CX_Complex)
DIM aux AS CX_Complex, aux2 AS CX_Complex
aux.re = modulus(c1) ^ n
aux.im = 0
aux2.re = COS(n * argument(c1))
aux2.im = SIN(n * argument(c1))
CX_times aux, aux2, result
END SUB

' raise a complex number to a complex power
' utilize the definition of the power with the EXP() and LOG()
' functions
SUB CX_powerCX (c1 AS CX_Complex, c2 AS CX_Complex, result AS CX_Complex)
DIM aux1 AS CX_Complex, aux2 AS CX_Complex
CX_ln c1, aux1 ' logarithm of the number to be raised ==> aux1
CX_times aux1, c2, aux2 ' multiply it with the exponent ==> aux2
CX_exp aux2, result ' raise e to the aux2 power
END SUB

' complex square root
' raise the argument to the power 1/2
SUB CX_sqrt (c1 AS CX_Complex, result AS CX_Complex)
DIM onehalf AS CX_Complex
onehalf.re = 1.0 / 2.0
onehalf.im = 0.0
CX_powerCX c1, onehalf, result
END SUB

SUB CX_neg (c1 AS CX_Complex, result AS CX_Complex)
result.re = -c1.re
result.im = -c1.im
END SUB




And the following is an even better version, with a more efficient complex square root routine:

Code: [Select]


' QB64 complex library
' Antti J Ylikoski and Matt "DSMan195276" 2013-03-05
'
' Define the type CX_Complex and operators whose result always is
' the last argument
'
' Very very straightforward translation from Java
' Originally written to carry out Fourier transforms in Java
'


TYPE CX_Complex
    re AS DOUBLE ' real part
    im AS DOUBLE ' imaginary part
    md AS DOUBLE ' modulus (polar rep)
    ar AS DOUBLE ' argument (polar rep)
END TYPE



'sum
SUB CX_plus (c1 AS CX_Complex, c2 AS CX_Complex, result AS CX_Complex)
result.re = c1.re + c2.re
result.im = c1.im + c2.im
END SUB

'difference
SUB CX_minus (c1 AS CX_Complex, c2 AS CX_Complex, result AS CX_Complex)
result.re = c1.re - c2.re
result.im = c1.im - c2.im
END SUB

' product
SUB CX_times (c1 AS CX_Complex, c2 AS CX_Complex, result AS CX_Complex)
result.re = c1.re * c2.re - c1.im * c2.im
result.im = c1.im * c2.re + c1.re * c2.im
END SUB

' division, quotient
SUB CX_div (c1 AS CX_Complex, c2 AS CX_Complex, result AS CX_Complex)
result.re = (c1.re * c2.re + c1.im * c2.im) / (c2.re ^ 2 + c2.im ^ 2)
result.im = (c1.im * c2.re - c1.re * c2.im) / (c2.re ^ 2 + c2.im ^ 2)
END SUB

' multiply by a real number
SUB CX_RealMul (c1 AS CX_Complex, v AS DOUBLE, result AS CX_Complex)
result.re = c1.re * v
result.im = c1.im * v
END SUB

' inverse, reciprocal
SUB CX_reciprocal (c1 AS CX_Complex, result AS CX_Complex)
DIM conj AS CX_Complex
CX_Conjugate c1, conj
CX_RealMul conj, 1.0 / (c1.re * c1.re + c1.im * c1.im), result
END SUB

'Conj is the complex conjugate
SUB CX_Conjugate (c1 AS CX_Complex, conj AS CX_Complex)
conj.re = c1.re
conj.im = -c1.im
END SUB

' square of a complex number
SUB CX_Square (c1 AS CX_Complex, result AS CX_Complex)
CX_times c1, c1, result
END SUB

' convert the representation to polar (rect is the default)
SUB CX_ConvPolar (c1 AS CX_Complex)
c1.md = SQR(c1.re ^ 2 + c1.im ^ 2)
c1.ar = ATN(c1.im / c1.re)
END SUB

' convert representation to rectangular
SUB CX_ConvRect (c1 AS CX_Complex)
c1.re = c1.md * COS(c1.ar)
c1.im = c1.md * SIN(c1.ar)
END SUB

' Some nontrivial functions of complex numbers.
' For the formulae see e. g. the Wikipedia.

' e to complex power
' exp(x + yj) = exp(x) * exp(yj)
'             = exp(x) * (cos(y) + j*sin(y))
SUB CX_exp (c1 AS CX_Complex, result AS CX_Complex)
DIM aux AS CX_Complex
aux.re = COS(c1.im) ' cos(y)
aux.im = SIN(c1.im) ' j*sin(y)
CX_RealMul aux, EXP(c1.re), result
END SUB

' natural logarithm of a complex number
SUB CX_ln (c1 AS CX_Complex, result AS CX_Complex)
DIM aux AS CX_Complex
aux.re = LOG(ABS(modulus(c1)))
aux.im = argument(c1)
result = aux
END SUB

' the modulus of a complex number
FUNCTION modulus (c1 AS CX_Complex)
CX_ConvPolar c1
modulus = c1.md
END FUNCTION

' the argument of a complex number
FUNCTION argument (c1 AS CX_Complex)
CX_ConvPolar c1
argument = c1.ar
END FUNCTION

' raise a complex number to an integer power
' the deMoivre formula
SUB CX_powerInt (c1 AS CX_Complex, n AS INTEGER, result AS CX_Complex)
DIM aux AS CX_Complex, aux2 AS CX_Complex
aux.re = modulus(c1) ^ n
aux.im = 0
aux2.re = COS(n * argument(c1))
aux2.im = SIN(n * argument(c1))
CX_times aux, aux2, result
END SUB

' raise a complex number to a complex power
' utilize the definition of the power with the EXP() and LOG()
' functions
SUB CX_powerCX (c1 AS CX_Complex, c2 AS CX_Complex, result AS CX_Complex)
DIM aux1 AS CX_Complex, aux2 AS CX_Complex
CX_ln c1, aux1 ' logarithm of the number to be raised ==> aux1
CX_times aux1, c2, aux2 ' multiply it with the exponent ==> aux2
CX_exp aux2, result ' raise e to the aux2 power
END SUB

' complex square root
' raise the argument to the power 1/2
SUB CX_sqrt (c1 AS CX_Complex, result AS CX_Complex)
DIM onehalf AS CX_Complex
onehalf.re = 1.0 / 2.0
onehalf.im = 0.0
CX_powerCX c1, onehalf, result
END SUB

' the negative of a complex number
SUB CX_neg (c1 AS CX_Complex, result AS CX_Complex) ' -z
result.re = -c1.re
result.im = -c1.im
END SUB

' the classical signum function
FUNCTION signum (x AS DOUBLE)
IF (x = 0.0) THEN
    signum = 0
ELSEIF (x < 0.0) THEN
    signum = -1
ELSE
    signum = 1
END IF
END FUNCTION

' another complex square root
SUB CX_sqrt2 (c1 AS CX_Complex, result AS CX_Complex) ' another sqrt()
DIM gamma AS DOUBLE
DIM delta AS DOUBLE
gamma = SQR((c1.re + modulus(c1)) / 2.0)
delta = signum(c1.im) * SQR((-c1.re + modulus(c1)) / 2.0)
result.re = gamma
result.im = delta
END SUB





« Last Edit: March 05, 2013, 11:35:08 PM by anttiylikoski »

Billbo

  • Sr. Member
  • ****
  • Posts: 293
    • Email
Re: Complex number library for the QB64
« Reply #7 on: March 06, 2013, 08:59:13 AM »
Hi All,

I am not a whiz on libraries. Would your library be a *.BM or *.BI file?

Bill

DSMan195276

  • Hero Member
  • *****
  • Posts: 1996
  • Yes
    • Email
Re: Complex number library for the QB64
« Reply #8 on: March 06, 2013, 09:03:28 AM »
Quote from: Billbo on March 06, 2013, 08:59:13 AM
Hi All,

I am not a whiz on libraries. Would your library be a *.BM or *.BI file?

Bill

Yes, technically. However, BI and BM files just contain straight QB64 code, so what is and what isn't a library is fairly vague. Just put the TYPE for CX_Complex in a Bi file and all the functions into a BM file and you're good to go.

Matt
"Cast your cares on the Lord and he will sustain you; he will never let the righteous be shaken" -- Psalm 55:22
QB64 Linux Installer

  • Print