• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!

[Snippet] Complex Numbers

Level 14
Joined
Dec 12, 2012
Messages
1,007
Oh okay.
I fear that someone do this.
I think I'll fix those static to members.

Alright.

Btw it would be nice to make use of polar coordinates a bit more convenient for the user. At the moment one can only read out the polar values after the complex number was created in cartesian coordinates...

Suggestion: Add the following methods:

JASS:
// Constructor for a complex number from polar coordinates
static method createPolar takes real abs, real phase returns thistype

// Setter for absolute value of the complex number
method operator abs= takes real value returns nothing

// Setter for phase value of the complex number
method operator arg= takes real value returns nothing

This way both cartesian and polar form can be used in a uniform and convenient way.


EDIT:

You are missing a call here:

JASS:
debug BJDebugMsg("thistype Error : Divide by 0!! Crashing thread")
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
But what to do if the user create a cartesian one and uses such method ?
It would be a mess.

Thats no problem at all.

It won't matter if the user used the cartesian constructor or the polar constructor, because internal the class will still only store the cartesian representation.

(One possible) sketch for setting the absolute value (or phase) of a cartesian Complex:

JASS:
1. Convert the cartesian to polar form
2. Set its absolute value/phase appropriate
3. Convert it back to cartesian and store those values

All nicely wrapped in the corresponding operator setters.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Updated.
Didn't make the polar functions yet.

Alright, looking forward for them.

You still have a call missing:

JASS:
debug BJDebugMsg("thistype Error : Divide by 0!! Crashing thread")

You could also make siginificantly decrease code size of the library if you use the constructor instead of allocate in your methods:

JASS:
method add takes thistype z returns thistype
    local thistype c = thistype.allocate()
    set c.re = this.re + z.re
    set c.im = this.im + z.im
    return c
endmethod

Can be rewriten as

JASS:
method add takes thistype z returns thistype
    return thistype.create(this.re + z.re, this.im + z.im)
endmethod

Changing all methods like this will save quite some lines of code. Like you did for example in the exp method:

JASS:
method exp takes nothing returns thistype
    return Complex.create(Pow(e, this.re)*Cos(this.im), Pow(e, this.re)*Sin(this.im))
endmethod

Although using thistype here would be preferable over Complex, to keep it consistent with the rest of the library.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
This is getting really nice. However, there are still some things to improve.


Most important, the sqrt method is still not working 100%...

JASS:
local Complex c = Complex.create(-9, 0)
call BJDebugMsg(c.sqrt().toString())

Should display 0 + 3j (since thats the principal root of -9), but instead displays 0 + 0j.

Suggestions:

JASS:
method toString takes nothing returns string
    local string s
    if (this.im > 0) then
        set s = R2S(this.re)+"+"+R2S(this.im)+"i"
    else
        set s = R2S(this.re)+" "+R2S(this.im)+"i"
    endif
    return s
endmethod

->

JASS:
method toString takes nothing returns string
    if (this.im > 0) then
        return R2S(this.re)+"+"+R2S(this.im)+"i"
    endif
    return R2S(this.re)+R2S(this.im)+"i" // No space required here
endmethod

Shorter and no temporary involved (the div method could also be shortened a bit by this). Also you should remove the space in the string for negative imaginary parts (see comment in the code above) such that both complex values look equal. Otherwise you have this:

JASS:
1.0-2.0i // good
1.0 +2.0i // additional space if im > 0, inconsistent


Minor Suggestions:

  • Your private constants (like pi) could be capitalized (see coding conventions)
  • Maybe a toStringPolar method would be nice too
  • Add a check for the polar constructor and the operator abs setter if someone tries to set a negative abs value (since thats undefined).
  • In the sqrt method change this.im==0 to this.im == 0 since you used spaces that way everywhere else to keep the coding consistent (same in div method).
  • You have sometimes two newlines between methods (for example between operators abs and arg), that looks a bit strange


EDIT:

IMO it would also make sense to overload the equality operator (but not the smaller operator)

JASS:
method operator== takes thistype z returns boolean

such that complex values can be compared easier.
 
Last edited:
Level 14
Joined
Dec 12, 2012
Messages
1,007
I didn't remember it is possible to override == in vJASS.

Well if I think a bit more about it, its maybe not a good idea to do this after all...

Basically there are two problems with this:

The first problem is that vJass is not typesafe when it comes to structs and people just use thistype interchangeable with integer instead of performing an explicit cast. So using an overloaded operator== might lead to difficult-to-find bugs.

The second problem is, that not all operators work. One can only define two operators, the rest is auto-generated (which would be a good thing in principal). The rules are like this:

  • If you define operator<, then operator> is defined automatically
  • If you define operator==, then operator!= is defined automatically

The problem is, that the operators <= and >= are never defined and you also have no way of defining these. So using them, will instead use the corresponding integer operators:

JASS:
struct Test 
    integer value
    static method create takes integer val returns thistype
        local thistype this = thistype.allocate()
        set this.value = val
        return this
    endmethod
    method operator< takes thistype other returns boolean
        return value < other.value
    endmethod
    method operator== takes thistype other returns boolean
        return value == other.value
    endmethod
endstruct

function foo takes nothing returns nothing
    Test t1 = Test.create(1)
    Test t2 = Test.create(2)

    if t1 == t2 then // calls Test.operator==
    endif
    if t1 > t2 then // calls Test.operator>
    endif
    if t1 >= t2 then // calls integer.operator>= !!!
    endif
endfunction


Although this problem would not apply to a Complex class because a Complex does not have an operator< anyways, its maybe best to not allow these operators at all...

So, due to these problems I leave this point open for discussion and ask for some additional opinions.
 
I think it is fine to have an equality operator (==). It is commonplace for classes in general, even if they don't have a comparison operator (e.g. using hash values).

But I agree that with lfh, using comparator operators can lead to bugs that would be very difficult to debug. I suppose something like a compareTo() method could be implemented instead (negative => less than, 0 => equal, positive => greater than).
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Trying to sort C is pointless.
But yeah I agreee about the common point about comparaison operators.

Yes, maybe changing the operator== to a method like equals is better due to the incomplete implementation of operator overloading in vJass... I also seems that those operators don't work correctly when used in combination with modules, so... Maybe its best not to use them at all.

Btw, there seems to be an error in your div method:

JASS:
local Complex c1 = Complex.create(-81, 81)
local Complex c2 = Complex.create(-9, 0)
call BJDebugMsg(c1.div(c2).toString())

Should display 9.000 - 9.000i, but instead displays 9.000 - 729.000i, so there is something wrong.

My bad: in the toString method you should use this.im >= 0 instead of this.im > 0, because zero is also displayed without sign, so we need to add the plus to the string as well.
 
elitism at its prime?

So how would manderbrot usable in wc3 in any sensible way anyway?

It would be a demo!
Demos are not meant to be usable, they're just meant to showcase something.
The Mandelbrot set is a complex quadratic map, so it's an example of something you could implement using a library like this.

I didn't know making suggestions implies elitism.
Tell me more about your insights into the human condition.
 
Top