Category talk:Wren-complex: Difference between revisions
(→Source code: Broadened the scope of the division and equality operators.) |
(→Complex numbers: Added a paragraph about support for complex matrices.) |
||
Line 4: | Line 4: | ||
Complex numbers can be constructed from two separate Nums, from a pair of Nums, from a Rat object, from a complex string or (by copying) from another Complex number . These representations can also be mixed in operations as long as the first operand is itself a Complex object. |
Complex numbers can be constructed from two separate Nums, from a pair of Nums, from a Rat object, from a complex string or (by copying) from another Complex number . These representations can also be mixed in operations as long as the first operand is itself a Complex object. |
||
Support for complex matrices is also included though is not as extensive as for real matrices in the Wren-matrix module. It does however include some extra methods (such as conjugate transpose) which only apply to complex matrices. |
|||
===Source code=== |
===Source code=== |
Revision as of 17:52, 26 November 2020
Complex numbers
This module aims to provide broadly similar functionality for complex numbers as already exists for real numbers. However, as the former - taken as a whole - are unordered, methods which require ordering have been omitted.
Complex numbers can be constructed from two separate Nums, from a pair of Nums, from a Rat object, from a complex string or (by copying) from another Complex number . These representations can also be mixed in operations as long as the first operand is itself a Complex object.
Support for complex matrices is also included though is not as extensive as for real matrices in the Wren-matrix module. It does however include some extra methods (such as conjugate transpose) which only apply to complex matrices.
Source code
<lang ecmascript>/* Module "complex.wren" */
import "/trait" for Cloneable
/* Complex represents a complex number of the form 'a + bi' where 'a' and 'b'
are both Nums. Complex objects are immutable.
- /
class Complex is Cloneable {
// Private helper function to check that 'o' is a suitable type and throw an error // otherwise. Real numbers, rationals and numeric strings are returned as complex numbers. static check_(o) { if (o is Complex) return o if (o is Num) return new_(o, 0) if ((o is List) && o.count == 2 && (o[0] is Num) && (o[1] is Num)) { return Complex.new_(o[0], o[1]) } if (o.type.toString == "Rat") return new_(r.toFloat, 0) if (o is String) return fromString(o) Fiber.abort("Argument must be a number, pair of numbers, a rational number or a complex string.") }
// Constants. static minusOne { Complex.new_(-1, 0) } static zero { Complex.new_( 0, 0) } static one { Complex.new_( 1, 0) } static two { Complex.new_( 2, 0) } static ten { Complex.new_(10, 0) } static imagMinusOne { Complex.new_( 0, -1) } static imagOne { Complex.new_( 0, 1) } static imagTwo { Complex.new_( 0, 2) } static imagTen { Complex.new_( 0, 10) } static i { Complex.new_( 0, 1) } // same as imagOne static pi { Complex.new_(Num.pi, 0) } static e { Complex.new_(2.71828182845904523536, 0) } static phi { Complex.new_(1.6180339887498948482, 0) } // golden ratio static tau { Complex.new_(1.6180339887498948482, 0) } // synonym for phi static ln2 { Complex.new_(0.69314718055994530942, 0) } // 2.log static ln10 { Complex.new_(2.30258509299404568402, 0) } // 10.log
// Determines whether a Complex object is always shown as such // or, if purely real, as a real. static showAsReal { __showAsReal } static showAsReal=(b) { __showAsReal = b }
// Constructs a new Complex object by passing it real and imaginary components. construct new(real, imag) { if (real.type != Num || imag.type != Num) System.print("Argument(s) must be numbers.") _real = real _imag = imag }
// Convenience method which constructs a new Complex object from a Num by passing it // just a real component. static new(real) { new(real, 0) }
// Private constructor which avoids type checking. construct new_(real, imag) { _real = real _imag = imag }
// Constructs a Complex object from an ordered pair of numbers [real, imag]. static fromPair(p) { if (p.type != List || p.count != 2 || p[0].type != Num || p[1].type != Num) { Fiber.abort("Argument must be an ordered pair of numbers.") } return Complex.new_(p[0], p[1]) }
// Constructs a Complex object from a string of the form '±a±bi', '±a' or '±bi' // where 'a' and 'b' are string representations of Nums. static fromString(s) { if (s.type != String) Fiber.abort("Argument must be a complex string.") s = s.trim() if (s == "") Fiber.abort("Invalid complex string.") s = s.replace("--", "+") if (s[0] == "+") s = s[1..-1] if (s == "") Fiber.abort("Invalid complex string.") s = s.replace("e+", "e") var neg = s[0] == "-" if (neg) { s = s[1..-1] if (s == "") Fiber.abort("Invalid complex string.") } var negExp = s.indexOf("e-") >= 0 if (negExp) s = s.replace("e-", "\v") if (s.indexOf("+") >= 0) { var split = s.split("+") if (split.count != 2) Fiber.abort("Invalid complex string.") if (negExp) for (i in 0..1) split[i] = split[i].replace("\v", "e-") var real = Num.fromString(split[0]) if (!real) Fiber.abort("Invalid real part.") if (neg) real = -real if (!split[1].endsWith("i")) Fiber.abort("Invalid complex string.") var imag = Num.fromString(split[1][0..-2]) if (!imag) Fiber.abort("Invalid imaginary part.") return Complex.new_(real, imag) } else if (s.indexOf("-") >= 0) { var split = s.split("-") if (split.count != 2) Fiber.abort("Invalid complex string.") if (negExp) for (i in 0..1) split[i] = split[i].replace("\v", "e-") var real = Num.fromString(split[0]) if (!real) Fiber.abort("Invalid real part.") if (neg) real = -real if (!split[1].endsWith("i")) Fiber.abort("Invalid complex string.") var imag = Num.fromString(split[1][0..-2]) if (!imag) Fiber.abort("Invalid imaginary part.") return Complex.new_(real, -imag) } else if (s.endsWith("i")) { if (negExp) s = s.replace("\v", "e-") var imag = Num.fromString(s[0..-2]) if (!imag) Fiber.abort("Invalid imaginary part.") if (neg) imag = -imag return Complex.new_(0, imag) } else { if (negExp) s = s.replace("\v", "e-") var real = Num.fromString(s) if (!real) Fiber.abort("Invalid real part.") if (neg) real = -real return Complex.new_(real, 0) } }
// Constructs a Complex object from a Rat object. static fromRat(r) { if (r.type.toString != "Rat") Fiber.abort("Argument must be a rational number.") return Complex.new_(r.toFloat, 0) }
// Constructs a Complex object from polar coordinates (r, theta). static fromPolar(r, theta) { if (r.type != Num || theta.type != Num) { Fiber.abort("Arguments must be numbers.") } return Complex.new_(r * theta.cos, r * theta.sin) }
// Basic properties. real { _real } // real component imag { _imag } // imaginary component
isInfinity { _real.isInfinity || _imag.isInfinty } // true if either part is infinite isNan { _real.isNan || imag.isNan } // true if either part is nan
// Returns whether real component is an integer and imaginary component is zero. isRealInteger { _real.isInteger && _imag == 0 }
// Returns whether imaginary component is an integer and real component is zero. isImagInteger { _imag.isInteger && _real == 0 }
// Returns the ordered pair [_real, _imag]. toPair { [_real, _imag] }
// Returns the polar coordinates of this instance [modulus, phase]. toPolar { [abs, phase] }
// Returns a new instance which negates the current one. - { Complex.new_(-_real, -_imag) }
// Returns the inverse or reciprocal of this instance. inverse { var denom = _real * _real + _imag * _imag return Complex.new_(_real/denom, -_imag/denom) }
// Arithmetic operators (work with real numbers, rational numbers, complex strings // as well as other complex numbers). Always return a new instance. +(o) { o = Complex.check_(o) return Complex.new_(_real + o.real, _imag + o.imag) }
-(o) { this + (-o) }
*(o) { o = Complex.check_(o) return Complex.new_( _real * o.real - _imag * o.imag, _real * o.imag + _imag * o.real ) }
/(o) { o = Complex.check_(o) var i = o.inverse return Complex.new_( _real * i.real - _imag * i.imag, _real * i.imag + _imag * i.real ) }
// Returns the absolute value or modulus of this instance. abs { (_real*_real + _imag*_imag).sqrt }
// Returns the phase or argument of the current instance in the range [-π, π]. phase { _imag.atan(_real) }
// Returns the complex conjugate of this instance conj { Complex.new_(_real, -_imag) }
// Returns the square of this instance. square { Complex.new_(_real * _real - _imag * _imag, _real * _imag * 2) }
// Returns the square root of this instance. sqrt { var m = abs var r = ((m + _real)/2).sqrt var i = ((m - _real)/2).sqrt if (_imag < 0) i = -i return Complex.new_(r, i) }
// Returns the base 'e' exponential of this instance. exp { var e = Complex.e.real.pow(_real) /* change to _real.exp from version 0.4.0 */ return Complex.new_(e * _imag.cos, e * _imag.sin) }
// Returns the natural logarithm of the current instance. log { var p = phase if (p > Num.pi) p = p - Num.pi*2 return Complex.new_(abs.log, p) }
// Returns the logarithm to the base 2 of the current instance. log2 { log / Complex.two.log }
// Returns the logarithm to the base 10 of the current instance. log10 { log / Complex.ten.log }
// Returns this instance to the power of the complex number 'e'. pow(e) { e = Complex.check_(e) return (log * e).exp }
// Returns the cosine of the current instance. cos { var i = Complex.i return ((i * this).exp + (i * (-this)).exp) / Complex.two }
// Returns the sine of the current instance. sin { var i = Complex.i return ((i * this).exp - (i * (-this)).exp) / Complex.imagTwo }
// Returns the tangent of the current instance. tan { sin / cos }
// Returns the arc cosine of the current instance. acos { var c = (Complex.one - square).sqrt c = this + c * Complex.imagMinusOne return c.log * Complex.i }
// Returns the arc sine of the current instance. asin { var c = (Complex.one - square).sqrt c = c + this * Complex.imagMinusOne return c.log * Complex.i }
// Returns the arc tangent of the current instance. atan { var a = Complex.new_(_real, _imag - 1) var b = Complex.new_(-_real, -_imag - 1) return (Complex.imagMinusOne * (a/b).log) / Complex.two }
// Returns the hyperbolic cosine of the current instance. cosh { (this.exp + (-this).exp)/Complex.two }
// Returns the hyperbolic sine of the current instance. sinh { (this.exp - (-this).exp)/Complex.two }
// Returns the hyperbolic tangent of the current instance. tanh { sinh/cosh }
// Returns the inverse hyperbolic cosine of the current instance. acosh { (this + (square - Complex.one).sqrt).log }
// Returns the inverse hyperbolic sine of the current instance. asinh { (this + (square + Complex.one).sqrt).log }
// Returns the inverse hyperbolic tangent of the current instance. atanh { var c = (this + Complex.one).log c = c - (-(this - Complex.one)).log return c / Complex.two }
// The inherited 'clone' method just returns 'this' as Complex objects are immutable. // If you need an actual copy use this method instead. copy() { Complex.new_(_real, _imag ) }
// Equality operators. ==(o) { o = Complex.check_(o) return _real == o.real && _imag == o.imag } !=(o) { !(this == o) }
// Returns the string representation of this Complex object depending on 'showAsReal'. toString { if (_real == -0) _real = 0 if (_imag == -0) _imag = 0 var s = (_imag >= 0) ? "%(_real) + %(_imag)" : "%(_real) - %(-_imag)" s = (_imag.abs != 1) ? s + "i" : s[0..-2] + "i" if (s.endsWith("- 0i")) s = s[0..-5] + "+ 0i" return (Complex.showAsReal && _imag == 0) ? s = s[0..-6] : s }
}
/* Complexes contains routines applicable to lists of complex numbers. */ class Complexes {
static sum(a) { a.reduce(Complex.zero) { |acc, x| acc + x } } static mean(a) { sum(a)/a.count } static prod(a) { a.reduce(Complex.one) { |acc, x| acc * x } }
}
// Type aliases for classes in case of any name clashes with other modules. var Complex_Complex = Complex var Complex_Complexes = Complexes var Complex_Cloneable = Cloneable // in case imported indirectly</lang>