FFT of 1D array of 1024 measurements

Hello
Is there a function such as fft(array)?
The array input comes from an Arduino.
The sound library contains an FFT possibility but the input comes from an audio input.
Thank you.
Jozef lodeweyckx

For what purpose? There is

https://processing.org/reference/libraries/sound/FFT.html

and, in the minim library:

http://code.compartmental.net/minim/fft_class_fft.html

There are also lots and lots of Java implementations of FFT:

Hello Jeremy
Thank you for taking the time to respond!My knowledge of software is not great and I use the processing book to learn the language .I saw that there is FFT used in the sound library but I do not know how to adapt it for an array.Could you write a few lines how to do that?
I have no knowledge how to incorporate Java in processing.Any guidelines are also welcome and my knowledge of java is also limited.
Yours sincerely
Jozef Lodeweyckx
PS:The data for the array I get through Arduino .

Well, those implementations do each process an array of data.

What kind of data are you getting, and why are you using FFT on it? What results are you hoping to get.

For the external FFT Java library that is not in Contributions Manager, put the jar in your sketch code folder and then use an import statement to import it – I believe that you can also drag the jar into your sketch window.

Note also that one of those Stackoverflow answers is a pure code solution, not a library – you can add it as a .java file and use the class, or adapt the java into Processing and add it as a pde tab to your sketch.

if you still need something,
i try to play with the version of

@author Orlando Selenu

what gives unusual results ( complex pairs )
also i not know if
the offset ( given by fft[0] and fft[1] ) is also 2 times as for the normal fft (DFT)?

to have a show i made a small graph for signal ( test data ) and fft.


and from the print see the pair problem.
but the good thing is, no .jar file, even the class thing i disabled.
just a function to call.

// https://discourse.processing.org/t/fft-of-1d-array-of-1024-measurements/7901/2

// from https://stackoverflow.com/questions/3287518/reliable-and-fast-fft-in-java
// https://web.archive.org/web/20150922044939/http://www.wikijava.org/wiki/The_Fast_Fourier_Transform_in_Java_%28part_1%29
// kll note: that is the complex version, so need 2 input arrays, and the output as complex pairs..
/**
 * @author Orlando Selenu
 *
 */

//kll class FFTbase {
/**
 * The Fast Fourier Transform (generic version, with NO optimizations).
 *
 * @param inputReal
 *            an array of length n, the real part
 * @param inputImag
 *            an array of length n, the imaginary part
 * @param DIRECT
 *            TRUE = direct transform, FALSE = inverse transform
 * @return a new array of length 2n
 */
double[] fft(double[] inputReal, double[] inputImag, boolean DIRECT) {
  // - n is the dimension of the problem
  // - nu is its logarithm in base e
  int n = inputReal.length;

  // If n is a power of 2, then ld is an integer (_without_ decimals)
  double ld = Math.log(n) / Math.log(2.0);

  // Here I check if n is a power of 2. If exist decimals in ld, I quit
  // from the function returning null.
  if (((int) ld) - ld != 0) {
    System.out.println("The number of elements is not a power of 2.");
    return null;
  }

  // Declaration and initialization of the variables
  // ld should be an integer, actually, so I don't lose any information in
  // the cast
  int nu = (int) ld;
  int n2 = n / 2;
  int nu1 = nu - 1;
  double[] xReal = new double[n];
  double[] xImag = new double[n];
  double tReal, tImag, p, arg, c, s;

  // Here I check if I'm going to do the direct transform or the inverse
  // transform.
  double constant;
  if (DIRECT)
    constant = -2 * Math.PI;
  else
    constant = 2 * Math.PI;

  // I don't want to overwrite the input arrays, so here I copy them. This
  // choice adds \Theta(2n) to the complexity.
  for (int i = 0; i < n; i++) {
    xReal[i] = inputReal[i];
    xImag[i] = inputImag[i];
  }

  // First phase - calculation
  int k = 0;
  for (int l = 1; l <= nu; l++) {
    while (k < n) {
      for (int i = 1; i <= n2; i++) {
        p = bitreverseReference(k >> nu1, nu);
        // direct FFT or inverse FFT
        arg = constant * p / n;
        c = Math.cos(arg);
        s = Math.sin(arg);
        tReal = xReal[k + n2] * c + xImag[k + n2] * s;
        tImag = xImag[k + n2] * c - xReal[k + n2] * s;
        xReal[k + n2] = xReal[k] - tReal;
        xImag[k + n2] = xImag[k] - tImag;
        xReal[k] += tReal;
        xImag[k] += tImag;
        k++;
      }
      k += n2;
    }
    k = 0;
    nu1--;
    n2 /= 2;
  }

  // Second phase - recombination
  k = 0;
  int r;
  while (k < n) {
    r = bitreverseReference(k, nu);
    if (r > k) {
      tReal = xReal[k];
      tImag = xImag[k];
      xReal[k] = xReal[r];
      xImag[k] = xImag[r];
      xReal[r] = tReal;
      xImag[r] = tImag;
    }
    k++;
  }

  // Here I have to mix xReal and xImag to have an array (yes, it should
  // be possible to do this stuff in the earlier parts of the code, but
  // it's here to readibility).
  double[] newArray = new double[xReal.length * 2];
  double radice = 1 / Math.sqrt(n);
  for (int i = 0; i < newArray.length; i += 2) {
    int i2 = i / 2;
    // I used Stephen Wolfram's Mathematica as a reference so I'm going
    // to normalize the output while I'm copying the elements.
    newArray[i] = xReal[i2] * radice;
    newArray[i + 1] = xImag[i2] * radice;
  }
  return newArray;
}

/**
 * The reference bitreverse function.
 */
int bitreverseReference(int j, int nu) {
  int j2;
  int j1 = j;
  int k = 0;
  for (int i = 1; i <= nu; i++) {
    j2 = j1 / 2;
    k = 2 * k + j1 - 2 * j2;
    j1 = j2;
  }
  return k;
}
//kll }  // kll disable class thing

// kll now we try to use that in our processing sketch

int zeroy = 110, welle=1, owelle=4, many = 512;
double[] signalr = new double[many];   //signal real
double[] signali = new double[many];   //signal imag // empty

double[] myfft  = new double[2*many];
double off = 0.5D; //0.5D;
float fftmul = 10.0;               // fft graph zoom

void make_signal() {
  float ang = welle*2*PI/many;
  for (int i=0; i<many; i++) signalr[i] = off + 0.5*sin(ang*i)+ 0.1*sin(owelle*ang*i);
}

void draw_signal() {
  stroke(0, 200, 0);
  for (int i=0; i<many; i++) point(10+i, zeroy - 100.0*(float)signalr[i]);
  stroke(200, 0, 0);
  for (int i=0; i<many; i++) line(10 +i, zeroy, 10+i, zeroy - fftmul*(float)myfft[i]);
}

void setup() {
  size(532, 220);
  make_signal();
  myfft = fft(signalr, signali, true);
  for (int i=0; i<10; i++) println("i "+i+" fft "+nf((float)myfft[i], 1, 1));
}

void draw() {
  background(200, 200, 0);
  draw_signal();
}


1 Like

Dear Jeremy
I am stunned by your reply!
Thank you so much!
I was wandering why they use for example “Math.log(2.0);” and not " log(2.0);" ?
What does .jar means?
I tested your example and it gives the same pictures.
Could I use it if the number of samples is 1600 and not 1024?
My data are real floating point numbers with no DC component.
I will let you know later what the result is for my data .
Thank you for your great support!
Yours sincerely
Jozef Lodeweyckx

Dear Jeremy
In the script I found the answer to the question : “number of samples”.
The script quits if the number of samples is not a power of 2.
Sorry for the question.
Yours sincerely
Jozef Lodeweyckx

Because for some annoying reason the Processing devs decided to use floats rather than doubles for Processing’s maths operations, unlike the built-in Java Math functions that use the more accurate double precision.

1 Like

Hello Neil
Thank you for your help!
Yours sincerely
Jozef Lodeweyckx

Dear Jeremy

I tried the function and was able to use it on my data which is of type float[] .

To transform it to double[] I inspired me on your brilliant example.(Thanks)

I came to the following basic principle:

//My data is type float[] and the FFT function needs double[]

//It worked but is this the best way to go?

//I have never seen an instruction (float)A[i] before !!!!!!!

//Could you advice?

double[] A=new double[3];//declare ,create an array type double[3]

float[] B={1.2 ,  3.4  , 5.6};//declare ,create,Assign an array type float[3]

float[]  C=new float[3];//declare,create an array type float[3]

println(B);//print to console array B each element type float

for (int i=0;i<3;i++) {A[i]=B[i];}//translate each element from type float to double

println(A);//print to console array A each element type double

for (int i=0;i<3;i++) {C[i]=(float)A[i];}//translate each element from double to float

println(C);//print to console array C each element of type float

exit();

Could you advice me if this is the best way ?

I have not found anywhere you instruction (float)A[i] .Is that java?

Should I make 2 function floatDouble() and doubleFloat() or is there a better way?

Thank you for your help!

Yours sincerely

Jozef Lodeweyckx

PS:I would like to improve my Java .Is there a good book or some course on the internet ?

or i give you the same as a float version:

// https://discourse.processing.org/t/fft-of-1d-array-of-1024-measurements/7901/2

// from https://stackoverflow.com/questions/3287518/reliable-and-fast-fft-in-java
// https://web.archive.org/web/20150922044939/http://www.wikijava.org/wiki/The_Fast_Fourier_Transform_in_Java_%28part_1%29
// kll note: that is the complex version, so need 2 input arrays, and the output as complex pairs..

//     rev 0.1 forum https://discourse.processing.org/t/fft-of-1d-array-of-1024-measurements/7901/5
//     rev 0.2 change from double to float
// kll, i have no idea what we loose?

/**
 * @author Orlando Selenu
 *
 */

//kll class FFTbase {
/**
 * The Fast Fourier Transform (generic version, with NO optimizations).
 *
 * @param inputReal
 *            an array of length n, the real part
 * @param inputImag
 *            an array of length n, the imaginary part
 * @param DIRECT
 *            TRUE = direct transform, FALSE = inverse transform
 * @return a new array of length 2n
 */
float[] fft(float[] inputReal, float[] inputImag, boolean DIRECT) {
  // - n is the dimension of the problem
  // - nu is its logarithm in base e
  int n = inputReal.length;

  // If n is a power of 2, then ld is an integer (_without_ decimals)
  float ld = log(n) / log(2.0);

  // Here I check if n is a power of 2. If exist decimals in ld, I quit
  // from the function returning null.
  if (((int) ld) - ld != 0) {
    System.out.println("The number of elements is not a power of 2.");
    return null;
  }

  // Declaration and initialization of the variables
  // ld should be an integer, actually, so I don't lose any information in
  // the cast
  int nu = (int) ld;
  int n2 = n / 2;
  int nu1 = nu - 1;
  float[] xReal = new float[n];
  float[] xImag = new float[n];
  float tReal, tImag, p, arg, c, s;

  // Here I check if I'm going to do the direct transform or the inverse
  // transform.
  float constant;
  if (DIRECT)
    constant = -2 * PI;
  else
    constant = 2 * PI;

  // I don't want to overwrite the input arrays, so here I copy them. This
  // choice adds \Theta(2n) to the complexity.
  for (int i = 0; i < n; i++) {
    xReal[i] = inputReal[i];
    xImag[i] = inputImag[i];
  }

  // First phase - calculation
  int k = 0;
  for (int l = 1; l <= nu; l++) {
    while (k < n) {
      for (int i = 1; i <= n2; i++) {
        p = bitreverseReference(k >> nu1, nu);
        // direct FFT or inverse FFT
        arg = constant * p / n;
        c = cos(arg);
        s = sin(arg);
        tReal = xReal[k + n2] * c + xImag[k + n2] * s;
        tImag = xImag[k + n2] * c - xReal[k + n2] * s;
        xReal[k + n2] = xReal[k] - tReal;
        xImag[k + n2] = xImag[k] - tImag;
        xReal[k] += tReal;
        xImag[k] += tImag;
        k++;
      }
      k += n2;
    }
    k = 0;
    nu1--;
    n2 /= 2;
  }

  // Second phase - recombination
  k = 0;
  int r;
  while (k < n) {
    r = bitreverseReference(k, nu);
    if (r > k) {
      tReal = xReal[k];
      tImag = xImag[k];
      xReal[k] = xReal[r];
      xImag[k] = xImag[r];
      xReal[r] = tReal;
      xImag[r] = tImag;
    }
    k++;
  }

  // Here I have to mix xReal and xImag to have an array (yes, it should
  // be possible to do this stuff in the earlier parts of the code, but
  // it's here to readibility).
  float[] newArray = new float[xReal.length * 2];
  float radice = 1 / sqrt(n);
  for (int i = 0; i < newArray.length; i += 2) {
    int i2 = i / 2;
    // I used Stephen Wolfram's Mathematica as a reference so I'm going
    // to normalize the output while I'm copying the elements.
    newArray[i] = xReal[i2] * radice;
    newArray[i + 1] = xImag[i2] * radice;
  }
  return newArray;
}

/**
 * The reference bitreverse function.
 */
int bitreverseReference(int j, int nu) {
  int j2;
  int j1 = j;
  int k = 0;
  for (int i = 1; i <= nu; i++) {
    j2 = j1 / 2;
    k = 2 * k + j1 - 2 * j2;
    j1 = j2;
  }
  return k;
}
//kll }  // kll disable class thing

// kll now we try to use that in our processing sketch

int zeroy = 110, welle=1, owelle=4, many = 512;
float[] signalr = new float[many];   //signal real
float[] signali = new float[many];   //signal imag // empty

float[] myfft  = new float[2*many];
float off = -0.25f;
float fftmul = 10.0;               // fft graph zoom

void make_signal() {
  float ang = welle*2*PI/many;
  for (int i=0; i<many; i++) signalr[i] = off + 0.5*cos(ang*i)+ 0.1*cos(owelle*ang*i);
}

void draw_signal() {
  stroke(0, 200, 0);
  for (int i=0; i<many; i++) point(10+i, zeroy - 100.0*signalr[i]);
  stroke(200, 0, 0);
  for (int i=0; i<many; i++) line(10 +i, zeroy, 10+i, zeroy - fftmul*myfft[i]);
}

void setup() {
  size(532, 220);
  make_signal();
  myfft = fft(signalr, signali, true);
  for (int i=0; i<10; i++) println("i "+i+" fft "+nf(myfft[i], 1, 1));
}

void draw() {
  background(200, 200, 0);
  draw_signal();
}

1 Like

Dear Jeremy
Thanks very much.
Yours sincerely
Jozef Lodeweyckx

In Java, you need to use the Math library and its Math.log() method. Processing makes this easier to read and write with a log() function built-in – although, as Neil indicates, it has lower numeric precision, so if you need high precision you use Math.log() anyway.

A Java ARchive: JAR (file format) - Wikipedia

If you have a compiled java library in .jar file format, you can drop it into a sketch to import it and start using it.

1 Like

Keep in mind that Processing is a dialect of Java. If you want to improve it, you might start with one of the Processing books, and the online documentation:

You might be interested in:

  • Make: Getting Started with Processing, Second Edition
  • Learning Processing, Second Edition: A Beginner’s Guide to Programming Images, Animation, and Interaction
  • Processing: A Programming Handbook for Visual Designers,
    Second Edition

Also, don’t forget that there are lots of learning resources on the Processing website – including Tutorials and many Examples. Just reading the reference will also teach you a lot.

If you want Java (not Processing) then there are thousands of learning Java sites and resources online. An overwhelming number. I’d suggest just browsing around top search results and picking one that looks appealing to you. Keep in mind that if you want to learn Java fundamentals that work in Processing sketches, however, that you are limited to Java 7 and parts of Java 8 – some aspects of the most recent Java language versions will not work.

https://www.google.com/search?q=learn+java+online

1 Like

it all started with your question about

FFT

and @jeremydouglass show you a
link list and i follow the one to
stackoverflow
and interested in the @OrlandoSelenu example i play with it.
now actually with this i lead you into this 2 problems

  • double math ( or float )
  • complex math
    but as it was in a prior millennium i learned about that
    i also need to start over, and i want show you what you might need to use the
    complex data result from above function. ( untested )
    you can calculate from the paired list the magnitude by

using the [n] and [n+1] value for n = 0,2,4,6…

so if you also want to dig into COMPLEX numbers / what is not your primary question

FFT from arduino data

code: Complex ( double and float )
// https://en.wikipedia.org/wiki/Complex_number
// kll 2/2019   untested!

class Complexd {                                                                    // version using double
  double r;   // the real part
  double i;   // the imaginary part

  public Complexd(double r, double i) {
    this.r = r;
    this.i = i;
  }

  public void cprint(String name,boolean trigo) {
    if (trigo )     println(name+" = "+nf((float)this.cmag(), 1, 3)+"*( cos("+nf((float)this.carg(), 1, 3)+") + i sin("+nf((float)this.carg(), 1, 3)+") )");
    else            println(name+" = ("+r+" + "+i+"i)");
  }

  public boolean cequal(Complexd b) {
    boolean same = false;
    if ( this.r == b.r && this.i == b.i) same = true;
    return same;
  }

  public Complexd cconj() {
    return new Complexd(r, -i);
  }

  public Complexd cadd(Complexd b) {
    double real = this.r + b.r;
    double img =  this.i + b.i;
    return new Complexd(real, img);
  }

  public Complexd csub(Complexd b) {
    double real = this.r - b.r;
    double img =  this.i - b.i;
    return new Complexd(real, img);
  }

  public Complexd cmul(Complexd b) {
    double real = this.r * b.r - this.i * b.i;
    double img =  this.r * b.i + this.i * b.r;
    return new Complexd(real, img);
  }

  public Complexd cdiv(Complexd b) {
    double real=0, img=0, smag=1;
    smag = (b.r * b.r + b.i * b.i);
    if ( smag != 0 ) {
      real = (this.r * b.r + this.i * b.i)/smag;
      img =  (this.i * b.r - this.r * b.i)/smag;
    }
    return new Complexd(real, img);
  }

  public double cmag() {
    double mag = Math.sqrt(this.r * this.r + this.i * this.i);
    return mag;
  }

  public double carg() {
    double arg = Math.atan2(this.i, this.r);
    return arg;
  }
}

// usage examples:   Complexd                                                          // version double
Complexd first = new Complexd(1, 1);
double firstmag = first.cmag();
double firstarg = first.carg();
// output
println("\nComplexd double");
first.cprint("cprint first",false);
println("firstmag "+nf((float)firstmag, 1, 3));
println("firstarg rad: "+nf((float)firstarg, 1, 3)+ " deg: "+degrees((float)firstarg));
first.cprint("cprint trigo first",true);

// math
println("_cmath: cadd");
Complexd resultadd =  first.cadd(first);
resultadd.cprint("resultadd",false);
println("_cmath: csub");
Complexd resultsub =  first.csub(first);
resultsub.cprint("resultsub",false);
println("_cmath: cmul");
Complexd resultmul =  first.cmul(first);
resultmul.cprint("resultmul",false);
println("_cmath: cdiv");
Complexd resultdiv =  first.cdiv(first);
resultdiv.cprint("resultdiv",false);
println("_cmath: cequal");
boolean eq = first.cequal(first);
println("first == first "+eq);
eq = first.cequal(resultmul);
println("first == resultmul "+eq);
println("_cmath: cconj");
Complexd firstconj =  first.cconj();
firstconj.cprint("firstconj",false);



class Complexf {                                                                 // version using float
  float r;   // the real part
  float i;   // the imaginary part

  public Complexf(float r, float i) {
    this.r = r;
    this.i = i;
  }

  public void cprint(String name,boolean trigo) {
    if (trigo )     println(name+" = "+nf((float)this.cmag(), 1, 3)+"*( cos("+nf((float)this.carg(), 1, 3)+") + i sin("+nf((float)this.carg(), 1, 3)+") )");
    else            println(name+" = ("+r+" + "+i+"i)");
  }

  public boolean cequal(Complexf b) {
    boolean same = false;
    if ( this.r == b.r && this.i == b.i) same = true;
    return same;
  }

  public Complexf cconj() {
    return new Complexf(r, -i);
  }

  public Complexf cadd(Complexf b) {
    float real = this.r + b.r;
    float img =  this.i + b.i;
    return new Complexf(real, img);
  }

  public Complexf csub(Complexf b) {
    float real = this.r - b.r;
    float img =  this.i - b.i;
    return new Complexf(real, img);
  }

  public Complexf cmul(Complexf b) {
    float real = this.r * b.r - this.i * b.i;
    float img =  this.r * b.i + this.i * b.r;
    return new Complexf(real, img);
  }

  public Complexf cdiv(Complexf b) {
    float real=0, img=0, smag=1;
    smag = (b.r * b.r + b.i * b.i);
    if ( smag != 0 ) {
      real = (this.r * b.r + this.i * b.i)/smag;
      img =  (this.i * b.r - this.r * b.i)/smag;
    }
    return new Complexf(real, img);
  }

  public float cmag() {
    float mag = sqrt(this.r * this.r + this.i * this.i);
    return mag;
  }

  public float carg() {
    float arg = atan2(this.i, this.r);
    return arg;
  }
}

// usage examples:   Complexf                                                  // version float
Complexf firstf = new Complexf(1, 1);
float firstmagf = firstf.cmag();
float firstargf = firstf.carg();
// output
println("\nComplexf float");
firstf.cprint("cprint firstf",false);
println("firstmag f"+nf(firstmagf, 1, 3));
println("firstargf rad: "+nf(firstargf, 1, 3)+ " deg: "+degrees(firstargf));
firstf.cprint("cprint trigo firstf",true);

// math
println("_cmath: cadd");
Complexf resultaddf =  firstf.cadd(firstf);
resultaddf.cprint("resultaddf",false);
println("_cmath: csub");
Complexf resultsubf =  firstf.csub(firstf);
resultsubf.cprint("resultsubf",false);
println("_cmath: cmul");
Complexf resultmulf =  firstf.cmul(firstf);
resultmulf.cprint("resultmulf",false);
println("_cmath: cdiv");
Complexf resultdivf =  firstf.cdiv(firstf);
resultdivf.cprint("resultdivf",false);
println("_cmath: cequal");
boolean eqf = firstf.cequal(firstf);
println("firstf == firstf "+eqf);
eqf = firstf.cequal(resultmulf);
println("firstf == resultmulf "+eqf);
println("_cmath: cconj");
Complexf firstconjf =  firstf.cconj();
firstconjf.cprint("firstconjf",false);


exit();

i must stress again that that is untested,
actually spec. mul and div of complex numbers and the shown results
i not understand.

first = (1.0 + 1.0i)
_cmath: cmul
first *first = (0.0 + 2.0i)
_cmath: cdiv
first /first  = (1.0 + 0.0i)

can that be right? any insight?

Dear Jeremy
I bought the :Processing A Programming Handbook for Visual Designers and Artists.It is very helpful.
Thank you so much for al the help.
Yours sincerely
Jozef Lodeweyckx

1 Like

Dear kll
The multiply and divide are OK.
A complex number can be visualized by plotting a vector whos real part is the x value and the y part is the complex value.So first =1+1i means x=1 and y is 1.This vector has an angle of 45 degrees or pi/4 in radians and a length(amplitude) of sqrt(x^2+y^2) or sqrt(2)=±1.41…
If you multiply 2 complex number the amplitude of the result is a complex numberwhos amplitude is the product of the amplitudes and the angle of the result is the sum of the two angles.
So for firstfirst= sqrt(2)sqrt(2)=2 for amplitude and angle=45deg+45deg=90deg.
If you draw the result it represent a vector on the y axis or 2i.
You can also calculate(trick) it by calculating (1+1i)
(1+1i)=1
1+11i+1i1+1i1i=1+1i+1i-1=2i.
Its like you calculate(a+b)
(c+d)=ac+ad+bc+bd .
In this calculation you assume that ii=-1 .
For division you divide the amplitudes and subtract the angles.
So (1+1i)/(1+1i)=sqrt(2)/sqrt(2)=1 for amplitude and 45 deg -45 deg for angle=0 .
The end result is a vector with an amplude of 1 and an angle of 0.
Graphical representation is x=1 and y=0 or 1+0
i .
So for division one has to represent the numbers with amplitude,angle and the trick for multiplication does not work
I hope this is helpful.
Yours sincerely
Jozef Lodeweyckx

ok, thanks, so obviously you not need above provided complex code / functions
as you are more fit in this.

now, tell us about what kind of data you get ? measure
and pls show us the resulting graphs signal & fft

You can use PVector for convenience in storing the two parts of a complex number. This is fairly common (past example).

Other complex number library resources include QScript:

From https://forum.processing.org/two/discussion/comment/94874/#Comment_94874 :

The QScript library has a file Complex.java which is an immutable implementation of a complex number class. You can see the API here: QScript: org.qscript.Complex Class Reference. Simply download/instal the library and locate the Java file in the source folder. Or you could just install the library and use the complex number class.

You can also use general Java resources, or roll your own – many options:

1 Like

Hello, I’m the author (Orlando Selenu). I just found this post. Hopefully, you solved your issues, otherwise, feel free to contact me.

2 Likes