// // ComplexMatrix.swift // Plinth // // Created by Daniel Clelland on 13/04/22. // import Foundation import Numerics public struct ComplexMatrix where Scalar: Real { public typealias Matrix = Plinth.Matrix public typealias Complex = Numerics.Complex public var real: Matrix public var imaginary: Matrix public init(real: Matrix, imaginary: Matrix) { self.real = real self.imaginary = imaginary } public init(shape: Shape, repeating element: Complex) { self.init(real: .init(shape: shape, repeating: element.real), imaginary: .init(shape: shape, repeating: element.imaginary)) } public init(shape: Shape, elements: [Complex]) { self.init(real: .init(shape: shape, elements: elements.map(\.real)), imaginary: .init(shape: shape, elements: elements.map(\.imaginary))) } public init(shape: Shape, _ closure: @autoclosure () throws -> Complex) rethrows { var elements: [Complex] = [] elements.reserveCapacity(shape.count) for _ in 0.. Complex) rethrows { var elements: [Complex] = [] elements.reserveCapacity(shape.count) for row in 0.. Complex { get { precondition(shape.contains(row: row, column: column)) return Complex(real[row, column], imaginary[row, column]) } set { precondition(shape.contains(row: row, column: column)) real[row, column] = newValue.real imaginary[row, column] = newValue.imaginary } } } extension ComplexMatrix: ExpressibleByFloatLiteral where Scalar == FloatLiteralType { public init(floatLiteral value: Scalar) { self.init(element: Complex(value)) } } extension ComplexMatrix: ExpressibleByArrayLiteral { public init(arrayLiteral elements: [Complex]...) { self.init(grid: elements) } } extension ComplexMatrix: CustomStringConvertible where Scalar: CustomStringConvertible { public var description: String { switch state { case .regular: return "[[" + grid.map { $0.map(\.description).joined(separator: ", ") }.joined(separator: "],\n [") + "]]" case .malformed(let malformation): return "Malformed \(type(of: self)): \(malformation)" } } } extension ComplexMatrix: Equatable where Scalar: Equatable { public static func == (left: ComplexMatrix, right: ComplexMatrix) -> Bool { return left.real == right.real && left.imaginary == right.imaginary } } extension ComplexMatrix: Hashable where Scalar: Hashable { public func hash(into hasher: inout Hasher) { hasher.combine(real) hasher.combine(imaginary) } } extension ComplexMatrix: Codable where Scalar: Codable { enum CodingKeys: String, CodingKey { case real case imaginary } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.real = try container.decode(Matrix.self, forKey: .real) self.imaginary = try container.decode(Matrix.self, forKey: .imaginary) if case .malformed(let malformation) = self.state { throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, debugDescription: "Malformed \(type(of: self)): \(malformation)")) } } public func encode(to encoder: Encoder) throws { if case .malformed(let malformation) = self.state { throw EncodingError.invalidValue(self, .init(codingPath: encoder.codingPath, debugDescription: "Malformed \(type(of: self)): \(malformation)")) } var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(real, forKey: .real) try container.encode(imaginary, forKey: .imaginary) } } extension ComplexMatrix: Collection { public typealias Index = Int public var startIndex: Index { return 0 } public var endIndex: Index { return Swift.min(real.count, imaginary.count) } public func index(after index: Index) -> Index { return index + 1 } public subscript(_ index: Index) -> Complex { return Complex(real[index], imaginary[index]) } } extension ComplexMatrix: BidirectionalCollection { public func index(before index: Index) -> Index { return index - 1 } public func reversed() -> ComplexMatrix { return ComplexMatrix(real: real.reversed(), imaginary: imaginary.reversed()) } }