001/******************************************************************************* 002 * This software is provided as a supplement to the authors' textbooks on digital 003 * image processing published by Springer-Verlag in various languages and editions. 004 * Permission to use and distribute this software is granted under the BSD 2-Clause 005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause). 006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved. 007 * Visit https://imagingbook.com for additional details. 008 ******************************************************************************/ 009 010package imagingbook.common.math; 011 012import java.util.Locale; 013 014import static imagingbook.common.math.Arithmetic.isZero; 015import static imagingbook.common.math.Arithmetic.sqr; 016 017/** 018 * <p> 019 * This class represents complex numbers. All instances are immutable. Methods are mostly defined to be compatible with 020 * org.apache.commons.math3.complex.Complex and (newer) org.apache.commons.numbers.complex.Complex. Arithmetic 021 * operations are generally more precise than with the Apache implementation. See also Appendix Sec. A.5 of [1]. 022 * </p> 023 * <p> 024 * [1] W. Burger, M.J. Burge, <em>Digital Image Processing – An Algorithmic Introduction</em>, 3rd ed, Springer 025 * (2022). 026 * </p> 027 * 028 * @author WB 029 * @version 2022/07/05 030 */ 031public class Complex { 032 033 /** Constant - real unit value (z = 1 + i 0). */ 034 public static final Complex ONE = new Complex(1, 0); 035 /** Constant - complex zero value (z = 0 + i 0). */ 036 public static final Complex ZERO = new Complex(0, 0); 037 /** Constant - imaginary unit value (z = 0 + i 1). */ 038 public static final Complex I = new Complex(0, 1); 039 040 /** The real part of this complex number (publicly accessible but read-only). */ 041 public final double re; 042 /** The imaginary part of this complex number (publicly accessible but read-only). */ 043 public final double im; 044 045 /** 046 * Constructor. 047 * @param re real part 048 * @param im imaginary part 049 */ 050 public Complex(double re, double im) { 051 this.re = re; 052 this.im = im; 053 } 054 055 /** 056 * Constructor. 057 * @param z a two-element {@code double} array with real and imaginary part 058 */ 059 public Complex(double[] z) { 060 this(z[0], z[1]); 061 } 062 063 /** 064 * Constructor. 065 * @param z complex quantity, which is duplicated 066 */ 067 public Complex(Complex z) { 068 this.re = z.re; 069 this.im = z.im; 070 } 071 072 /** 073 * Constructor, creates a complex quantity on the unit circle with angle {@code phi}: 074 * {@code e^(i * phi) = cos(phi) + i * sin(phi)}. 075 * 076 * @param phi the angle 077 * @see #arg() 078 */ 079 public Complex(double phi) { 080 this.re = Math.cos(phi); 081 this.im = Math.sin(phi); 082 } 083 084 // ------------------------------------------------------------------- 085 086 /** 087 * Returns the absolute value of this complex number, i.e., its radius or distance from the origin. 088 * 089 * @return the absolute value 090 */ 091 public double abs() { 092 return Math.hypot(re, im); 093 } 094 095 /** 096 * Returns the squared absolute value of this complex number, i.e., its squared radius or distance from the origin. 097 * 098 * @return the squared absolute value 099 */ 100 public double abs2() { 101 return sqr(re) + sqr(im); 102 } 103 104 /** 105 * Returns the 'argument' of this complex number, i.e., its angle w.r.t. to the real axis. 106 * 107 * @return the argument (in radians) 108 */ 109 public double arg() { 110 return Math.atan2(im, re); 111 } 112 113 /** 114 * Returns the conjugate {@code z*} of this complex number, i.e, if {@code z = a + i b} then {@code z* = a - i b}. 115 * 116 * @return the complex conjugate 117 */ 118 public Complex conjugate() { 119 return new Complex(this.re, -this.im); 120 } 121 122 /** 123 * Adds a complex quantity to this complex number and returns a new complex number. 124 * 125 * @param z complex value 126 * @return the sum of this complex number and {@code z} 127 */ 128 public Complex add(Complex z) { 129 return new Complex(this.re + z.re, this.im + z.im); 130 } 131 132 /** 133 * Rotates this complex number by angle {@code phi} and returns the resulting complex number. 134 * 135 * @param phi the angle (in radians) 136 * @return the rotated complex value 137 */ 138 public Complex rotate(double phi) { 139 return this.multiply(new Complex(phi)); 140 } 141 142 @Override 143 public String toString() { 144 return String.format(Locale.US, "(%.9f, %.9f)", re, im); 145 } 146 147 /** 148 * Returns true if the real or imaginary component of this complex number is {@code NaN}. 149 * 150 * @return true if {@code NaN}, otherwise false 151 */ 152 public boolean isNaN() { 153 return Double.isNaN(this.getRe()) || Double.isNaN(this.getIm()); 154 } 155 156 /** 157 * Returns the real part of this complex number. 158 * 159 * @return the real part 160 */ 161 public double getRe() { 162 return this.re; 163 } 164 165 /** 166 * Returns the imaginary part of this complex number. 167 * @return the imaginary part 168 */ 169 public double getIm() { 170 return this.im; 171 } 172 173 // ------------------------------------------------------- 174 175 /** 176 * Multiplies this complex number with another complex quantity and returns a new complex number. 177 * 178 * @param z a complex quantity 179 * @return this complex number multiplied by {@code z} 180 */ 181 public Complex multiply(Complex z) { 182 // (x1 + i y1)(x2 + i y2) = (x1 x2 + y1 y2) + i (x1 y2 + y1 x2) 183 final double x = this.re * z.re - this.im * z.im; 184 final double y = this.re * z.im + this.im * z.re; 185 return new Complex(x, y); 186 } 187 188 /** 189 * Multiplies this complex number with the scalar factor {@code s} and returns a new complex number. 190 * 191 * @param s a scalar factor 192 * @return this complex number multiplied by {@code s} 193 */ 194 public Complex multiply(double s) { 195 return new Complex(this.re * s, this.im * s); 196 } 197 198 /** 199 * Returns of value of this complex number ({@code z}) raised to the power {@code k} (integer). 200 * 201 * @param k the integer exponent (≥ 0) 202 * @return {@code z^k} 203 */ 204 public Complex pow(int k) { 205 if (k < 0) throw new IllegalArgumentException("exponent k >= 0 expected"); 206 Complex prod = new Complex(1, 0); 207 for (int i = 0; i < k; i++) { 208 prod = prod.multiply(this); 209 } 210 return prod; 211 } 212 213 /** 214 * Returns a 2-element array with the real and imaginary part of this complex number. 215 * 216 * @return (re, im) 217 */ 218 public double[] toArray() { 219 return new double[] {this.re, this.im}; 220 } 221 222 /** 223 * Checks if the given {@link Object} is equal to this {@link Complex} quantity. Calls 224 * {@link #equals(Complex, double)} if the argument is of type {@link Complex}, otherwise {@code null} is returned. 225 */ 226 @Override 227 public boolean equals(Object other) { 228 if (other == this) { 229 return true; 230 } 231 if (other instanceof Complex) { 232// final Complex z = (Complex) other; 233// return Double.compare(this.re, z.re) == 0 && Double.compare(this.im, z.im) == 0; 234 return this.equals((Complex) other, Arithmetic.EPSILON_DOUBLE); 235 } 236 return false; 237 } 238 239 /** 240 * Checks if the given {@link Complex} quantity is equal to this {@link Complex} quantity. 241 * 242 * @param z another {@link Complex} quantity 243 * @param tolerance the maximum difference of real and imaginary parts 244 * @return true if the two complex quantities are sufficiently close, false otherwise 245 */ 246 public boolean equals(Complex z, double tolerance) { 247 return isZero(this.re - z.re, tolerance) 248 && isZero(this.im - z.im, tolerance); 249 } 250 251 /** 252 * Checks if the given complex quantity is equal to this {@link Complex} quantity, using the default tolerance 253 * ({@link Arithmetic#EPSILON_DOUBLE}). 254 * 255 * @param re real part of other complex quantity 256 * @param im imaginary part of other complex quantity 257 * @return true if the two complex quantities are sufficiently close, false otherwise 258 */ 259 public boolean equals(double re, double im) { 260 //return Double.compare(this.re, re) == 0 && Double.compare(this.im, im) == 0; 261 return this.equals(re, im, Arithmetic.EPSILON_DOUBLE); 262 } 263 264 /** 265 * Checks if the given complex quantity is equal to this {@link Complex} quantity, using the specified tolerance. 266 * 267 * @param re real part of other complex quantity 268 * @param im imaginary part of other complex quantity 269 * @param tolerance the maximum difference of real and imaginary parts 270 * @return true if the two complex quantities are sufficiently close, false otherwise 271 */ 272 public boolean equals(double re, double im, double tolerance) { 273 return isZero(this.re - re, tolerance) 274 && isZero(this.im - im, tolerance); 275 } 276 277}