Skip to content

Commit

Permalink
Add complex math.
Browse files Browse the repository at this point in the history
  • Loading branch information
igr committed Mar 23, 2024
1 parent a1fd5e5 commit b3c1e8a
Show file tree
Hide file tree
Showing 5 changed files with 501 additions and 0 deletions.
198 changes: 198 additions & 0 deletions gartwork/src/main/kotlin/dev/oblac/gart/math/Complex.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package dev.oblac.gart.math

import kotlin.math.*


/**
* Imaginary unit.
*/
val i = Complex(0.0, 1.0)

/**
* Complex exponential.
*/
fun exp(c: Complex): Complex {
val e = exp(c.real)
return Complex(e * cos(c.img), e * sin(c.img))
}

/**
* Hyperbolic sine.
*/
fun sinh(c: Complex) = (exp(c) - exp(-c)) / 2

/**
* Hyperbolic cosine.
*/
fun cosh(c: Complex) = (exp(c) + exp(-c)) / 2

/**
* Hyperbolic tangent.
*/
fun tanh(c: Complex) = sinh(c) / cosh(c)

/**
* Hyperbolic cotangent.
*/
fun coth(c: Complex) = cosh(c) / sinh(c)

/**
* Complex cosine.
*/
fun cos(c: Complex) = (exp(i * c) + exp(-i * c)) / 2.0

/**
* Complex sine.
*/
fun sin(c: Complex) = i * (exp(-i * c) - exp(i * c)) / 2.0

/**
* Complex tangent.
*/
fun tan(c: Complex) = sin(c) / cos(c)

/**
* Complex cotangent.
*/
fun cot(c: Complex) = cos(c) / sin(c)

/**
* Complex secant.
*/
fun sec(c: Complex) = Complex.ONE / cos(c)

/**
* The natural logarithm on the principal branch.
*/
fun ln(c: Complex) = Complex(ln(c.abs()), c.phase())

/**
* Roots of unity.
*/
fun roots(n: Int) =
(1..n).map { exp(i * 2 * PI * it / n) }

operator fun Number.plus(c: Complex) = Complex(this.toDouble() + c.real, c.img)

operator fun Number.minus(c: Complex) = Complex(this.toDouble() - c.real, -c.img)

operator fun Number.times(c: Complex) = Complex(this.toDouble() * c.real, this.toDouble() * c.img)

operator fun Number.div(c: Complex) = Complex.ONE / c

/**
* Defines complex numbers and their algebraic operations.
* @param real the real component
* @param img the imaginary component
*/
class Complex(val real: Double, val img: Double) {

constructor(real: Number, img: Number) : this(real.toDouble(), img.toDouble())

override fun equals(other: Any?): Boolean {
return (other is Complex && real == other.real && img == other.img)
}

override fun hashCode(): Int {
return real.hashCode() * 31 + img.hashCode()
}

operator fun unaryMinus() = Complex(-real, -img)

operator fun plus(c: Complex) = Complex(real + c.real, img + c.img)

operator fun plus(n: Number) = Complex(real + n.toDouble(), img)

operator fun minus(c: Complex) = Complex(real - c.real, img - c.img)

operator fun minus(n: Number) = Complex(real - n.toDouble(), img)

operator fun times(c: Complex) = Complex(real * c.real - img * c.img, real * c.img + img * c.real)

operator fun times(n: Number) = Complex(n.toDouble() * real, n.toDouble() * img)

operator fun div(n: Number) = Complex(real / n.toDouble(), img / n.toDouble())

operator fun div(c: Complex): Complex {
val den = c.normSquared()
if (isPracticallyZero(den)) {
return this / 0 // todo make this consistent with division by zero number
}
val num = this * c.conjugate()
return num / den
}

operator fun component1() = real
operator fun component2() = img

/**
* Complex conjugate = x-y*i.
*/
fun conjugate() = Complex(real, -img)

fun normSquared() = real * real + img * img

fun abs(): Double = sqrt(this.normSquared())

fun phase(): Double = atan(img / real)

fun pow(a: Double) = exp(ln(this) * a)

fun pow(a: Number) = exp(ln(this) * a)

fun pow(a: Complex) = exp(ln(this) * a)

override fun toString(): String {
return when {
isPracticallyZero(img) -> "$real"
isPracticallyZero(real) -> "${img}i"
img < 0 -> "$real-${-img}i"
else -> "${real}+${img}i"
}
}

private fun isPracticallyZero(d: Double) = abs(d) < DEFAULT_TOLERANCE

companion object {
/**
* Complex 0 = 0 + 0i
*/
val ZERO = Complex(0.0, 0.0)

/**
* Complex 1 = 1 + 0i
*/
val ONE = Complex(1.0, 0.0)

const val DEFAULT_TOLERANCE = 1.0E-15

fun fromNumber(n: Number) = Complex(n.toDouble(), 0.0)

fun fromPolar(radius: Double, theta: Double): Complex = radius * exp(i * theta)

}

/**
* Tests if the norm of the complex number is smaller than the given tolerance
*/
fun isZero(tolerance: Double) = this.abs() < tolerance

infix fun to(exponent: Int): Complex {
if (exponent == 0) {
return ONE
}
if (exponent == 1) {
return this
}
val half = to(exponent / 2)
return if (exponent.isEven()) {
half * half
} else {
half * half * this
}
}

infix fun to(exponent: Complex) = this.pow(exponent)

infix fun to(exponent: Number) = this.pow(exponent)
}
19 changes: 19 additions & 0 deletions gartwork/src/main/kotlin/dev/oblac/gart/math/ComplexField.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dev.oblac.gart.math

class ComplexField(
val xFrom: Float,
val xTo: Float,
val yFrom: Float,
val yTo: Float,
val width: Int,
val height: Int
) {
private val xStep = (xTo - xFrom) / width
private val yStep = (yTo - yFrom) / height

private val data = Array(width) { x ->
Array(height) { y ->
Complex(xFrom + x * xStep, yFrom + y * yStep)
}
}
}
Loading

0 comments on commit b3c1e8a

Please sign in to comment.