From b326df11a9da01127dc157630b42e722c12e6e3a Mon Sep 17 00:00:00 2001 From: Eric Willigers Date: Fri, 11 Sep 2026 08:23:07 +1000 Subject: [PATCH] Add `complex-numbers` exercise --- config.json | 8 + .../complex-numbers/.docs/instructions.md | 100 ++++++++ .../complex-numbers/.meta/config.json | 19 ++ .../complex-numbers/.meta/example.zig | 76 ++++++ .../practice/complex-numbers/.meta/tests.toml | 130 ++++++++++ .../complex-numbers/complex_numbers.zig | 65 +++++ .../complex-numbers/test_complex_numbers.zig | 233 ++++++++++++++++++ generators/exercises/complex_numbers.py | 63 +++++ 8 files changed, 694 insertions(+) create mode 100644 exercises/practice/complex-numbers/.docs/instructions.md create mode 100644 exercises/practice/complex-numbers/.meta/config.json create mode 100644 exercises/practice/complex-numbers/.meta/example.zig create mode 100644 exercises/practice/complex-numbers/.meta/tests.toml create mode 100644 exercises/practice/complex-numbers/complex_numbers.zig create mode 100644 exercises/practice/complex-numbers/test_complex_numbers.zig create mode 100644 generators/exercises/complex_numbers.py diff --git a/config.json b/config.json index b3686228..48202e6b 100644 --- a/config.json +++ b/config.json @@ -979,6 +979,14 @@ "prerequisites": [], "difficulty": 5 }, + { + "slug": "complex-numbers", + "name": "Complex Numbers", + "uuid": "d79f6456-1d43-4cc7-a588-6870117e8135", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, { "slug": "food-chain", "name": "Food Chain", diff --git a/exercises/practice/complex-numbers/.docs/instructions.md b/exercises/practice/complex-numbers/.docs/instructions.md new file mode 100644 index 00000000..2b8a7a49 --- /dev/null +++ b/exercises/practice/complex-numbers/.docs/instructions.md @@ -0,0 +1,100 @@ +# Instructions + +A **complex number** is expressed in the form `z = a + b * i`, where: + +- `a` is the **real part** (a real number), + +- `b` is the **imaginary part** (also a real number), and + +- `i` is the **imaginary unit** satisfying `i^2 = -1`. + +## Operations on Complex Numbers + +### Conjugate + +The conjugate of the complex number `z = a + b * i` is given by: + +```text +zc = a - b * i +``` + +### Absolute Value + +The absolute value (or modulus) of `z` is defined as: + +```text +|z| = sqrt(a^2 + b^2) +``` + +The square of the absolute value is computed as the product of `z` and its conjugate `zc`: + +```text +|z|^2 = z * zc = a^2 + b^2 +``` + +### Addition + +The sum of two complex numbers `z1 = a + b * i` and `z2 = c + d * i` is computed by adding their real and imaginary parts separately: + +```text +z1 + z2 = (a + b * i) + (c + d * i) + = (a + c) + (b + d) * i +``` + +### Subtraction + +The difference of two complex numbers is obtained by subtracting their respective parts: + +```text +z1 - z2 = (a + b * i) - (c + d * i) + = (a - c) + (b - d) * i +``` + +### Multiplication + +The product of two complex numbers is defined as: + +```text +z1 * z2 = (a + b * i) * (c + d * i) + = (a * c - b * d) + (b * c + a * d) * i +``` + +### Reciprocal + +The reciprocal of a non-zero complex number is given by: + +```text +1 / z = 1 / (a + b * i) + = a / (a^2 + b^2) - b / (a^2 + b^2) * i +``` + +### Division + +The division of one complex number by another is given by: + +```text +z1 / z2 = z1 * (1 / z2) + = (a + b * i) / (c + d * i) + = (a * c + b * d) / (c^2 + d^2) + (b * c - a * d) / (c^2 + d^2) * i +``` + +### Exponentiation + +Raising _e_ (the base of the natural logarithm) to a complex exponent can be expressed using Euler's formula: + +```text +e^(a + b * i) = e^a * e^(b * i) + = e^a * (cos(b) + i * sin(b)) +``` + +## Implementation Requirements + +Given that you should not use built-in support for complex numbers, implement the following operations: + +- **addition** of two complex numbers +- **subtraction** of two complex numbers +- **multiplication** of two complex numbers +- **division** of two complex numbers +- **conjugate** of a complex number +- **absolute value** of a complex number +- **exponentiation** of _e_ (the base of the natural logarithm) to a complex number diff --git a/exercises/practice/complex-numbers/.meta/config.json b/exercises/practice/complex-numbers/.meta/config.json new file mode 100644 index 00000000..c368f491 --- /dev/null +++ b/exercises/practice/complex-numbers/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "complex_numbers.zig" + ], + "test": [ + "test_complex_numbers.zig" + ], + "example": [ + ".meta/example.zig" + ] + }, + "blurb": "Implement complex numbers.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Complex_number" +} diff --git a/exercises/practice/complex-numbers/.meta/example.zig b/exercises/practice/complex-numbers/.meta/example.zig new file mode 100644 index 00000000..383135b0 --- /dev/null +++ b/exercises/practice/complex-numbers/.meta/example.zig @@ -0,0 +1,76 @@ +const std = @import("std"); +const math = std.math; + +/// Returns a complex number type whose real and imaginary parts are of the +/// float type `T`. +pub fn Complex(comptime T: type) type { + return struct { + const Self = @This(); + + real: T, + imag: T, + + /// Initializes a complex number with the given real and imaginary parts. + pub fn init(real: T, imag: T) Self { + return .{ + .real = real, + .imag = imag, + }; + } + + /// Returns the sum of two complex numbers. + pub fn add(self: Self, other: Self) Self { + return .{ + .real = self.real + other.real, + .imag = self.imag + other.imag, + }; + } + + /// Returns the difference of two complex numbers. + pub fn sub(self: Self, other: Self) Self { + return .{ + .real = self.real - other.real, + .imag = self.imag - other.imag, + }; + } + + /// Returns the product of two complex numbers. + pub fn mul(self: Self, other: Self) Self { + return .{ + .real = self.real * other.real - self.imag * other.imag, + .imag = self.imag * other.real + self.real * other.imag, + }; + } + + /// Returns the quotient of two complex numbers. + pub fn div(self: Self, other: Self) Self { + const denominator = other.real * other.real + other.imag * other.imag; + return .{ + .real = (self.real * other.real + self.imag * other.imag) / denominator, + .imag = (self.imag * other.real - self.real * other.imag) / denominator, + }; + } + + /// Returns the complex conjugate. + pub fn conjugate(self: Self) Self { + return .{ + .real = self.real, + .imag = -self.imag, + }; + } + + /// Returns the absolute value (modulus). + pub fn abs(self: Self) T { + return math.hypot(self.real, self.imag); + } + + /// Returns the complex exponential function of the number. + pub fn exp(self: Self) Self { + const magnitude = @exp(self.real); + return .{ + .real = magnitude * @cos(self.imag), + .imag = magnitude * @sin(self.imag), + }; + } + }; +} diff --git a/exercises/practice/complex-numbers/.meta/tests.toml b/exercises/practice/complex-numbers/.meta/tests.toml new file mode 100644 index 00000000..dffb1f2a --- /dev/null +++ b/exercises/practice/complex-numbers/.meta/tests.toml @@ -0,0 +1,130 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[9f98e133-eb7f-45b0-9676-cce001cd6f7a] +description = "Real part -> Real part of a purely real number" + +[07988e20-f287-4bb7-90cf-b32c4bffe0f3] +description = "Real part -> Real part of a purely imaginary number" + +[4a370e86-939e-43de-a895-a00ca32da60a] +description = "Real part -> Real part of a number with real and imaginary part" + +[9b3fddef-4c12-4a99-b8f8-e3a42c7ccef6] +description = "Imaginary part -> Imaginary part of a purely real number" + +[a8dafedd-535a-4ed3-8a39-fda103a2b01e] +description = "Imaginary part -> Imaginary part of a purely imaginary number" + +[0f998f19-69ee-4c64-80ef-01b086feab80] +description = "Imaginary part -> Imaginary part of a number with real and imaginary part" + +[a39b7fd6-6527-492f-8c34-609d2c913879] +description = "Imaginary unit" + +[9a2c8de9-f068-4f6f-b41c-82232cc6c33e] +description = "Arithmetic -> Addition -> Add purely real numbers" + +[657c55e1-b14b-4ba7-bd5c-19db22b7d659] +description = "Arithmetic -> Addition -> Add purely imaginary numbers" + +[4e1395f5-572b-4ce8-bfa9-9a63056888da] +description = "Arithmetic -> Addition -> Add numbers with real and imaginary part" + +[1155dc45-e4f7-44b8-af34-a91aa431475d] +description = "Arithmetic -> Subtraction -> Subtract purely real numbers" + +[f95e9da8-acd5-4da4-ac7c-c861b02f774b] +description = "Arithmetic -> Subtraction -> Subtract purely imaginary numbers" + +[f876feb1-f9d1-4d34-b067-b599a8746400] +description = "Arithmetic -> Subtraction -> Subtract numbers with real and imaginary part" + +[8a0366c0-9e16-431f-9fd7-40ac46ff4ec4] +description = "Arithmetic -> Multiplication -> Multiply purely real numbers" + +[e560ed2b-0b80-4b4f-90f2-63cefc911aaf] +description = "Arithmetic -> Multiplication -> Multiply purely imaginary numbers" + +[4d1d10f0-f8d4-48a0-b1d0-f284ada567e6] +description = "Arithmetic -> Multiplication -> Multiply numbers with real and imaginary part" + +[b0571ddb-9045-412b-9c15-cd1d816d36c1] +description = "Arithmetic -> Division -> Divide purely real numbers" + +[5bb4c7e4-9934-4237-93cc-5780764fdbdd] +description = "Arithmetic -> Division -> Divide purely imaginary numbers" + +[c4e7fef5-64ac-4537-91c2-c6529707701f] +description = "Arithmetic -> Division -> Divide numbers with real and imaginary part" + +[c56a7332-aad2-4437-83a0-b3580ecee843] +description = "Absolute value -> Absolute value of a positive purely real number" + +[cf88d7d3-ee74-4f4e-8a88-a1b0090ecb0c] +description = "Absolute value -> Absolute value of a negative purely real number" + +[bbe26568-86c1-4bb4-ba7a-da5697e2b994] +description = "Absolute value -> Absolute value of a purely imaginary number with positive imaginary part" + +[3b48233d-468e-4276-9f59-70f4ca1f26f3] +description = "Absolute value -> Absolute value of a purely imaginary number with negative imaginary part" + +[fe400a9f-aa22-4b49-af92-51e0f5a2a6d3] +description = "Absolute value -> Absolute value of a number with real and imaginary part" + +[fb2d0792-e55a-4484-9443-df1eddfc84a2] +description = "Complex conjugate -> Conjugate a purely real number" + +[e37fe7ac-a968-4694-a460-66cb605f8691] +description = "Complex conjugate -> Conjugate a purely imaginary number" + +[f7704498-d0be-4192-aaf5-a1f3a7f43e68] +description = "Complex conjugate -> Conjugate a number with real and imaginary part" + +[6d96d4c6-2edb-445b-94a2-7de6d4caaf60] +description = "Complex exponential function -> Euler's identity/formula" + +[2d2c05a0-4038-4427-a24d-72f6624aa45f] +description = "Complex exponential function -> Exponential of 0" + +[ed87f1bd-b187-45d6-8ece-7e331232c809] +description = "Complex exponential function -> Exponential of a purely real number" + +[08eedacc-5a95-44fc-8789-1547b27a8702] +description = "Complex exponential function -> Exponential of a number with real and imaginary part" + +[d2de4375-7537-479a-aa0e-d474f4f09859] +description = "Complex exponential function -> Exponential resulting in a number with real and imaginary part" + +[06d793bf-73bd-4b02-b015-3030b2c952ec] +description = "Operations between real numbers and complex numbers -> Add real number to complex number" + +[d77dbbdf-b8df-43f6-a58d-3acb96765328] +description = "Operations between real numbers and complex numbers -> Add complex number to real number" + +[20432c8e-8960-4c40-ba83-c9d910ff0a0f] +description = "Operations between real numbers and complex numbers -> Subtract real number from complex number" + +[b4b38c85-e1bf-437d-b04d-49bba6e55000] +description = "Operations between real numbers and complex numbers -> Subtract complex number from real number" + +[dabe1c8c-b8f4-44dd-879d-37d77c4d06bd] +description = "Operations between real numbers and complex numbers -> Multiply complex number by real number" + +[6c81b8c8-9851-46f0-9de5-d96d314c3a28] +description = "Operations between real numbers and complex numbers -> Multiply real number by complex number" + +[8a400f75-710e-4d0c-bcb4-5e5a00c78aa0] +description = "Operations between real numbers and complex numbers -> Divide complex number by real number" + +[9a867d1b-d736-4c41-a41e-90bd148e9d5e] +description = "Operations between real numbers and complex numbers -> Divide real number by complex number" diff --git a/exercises/practice/complex-numbers/complex_numbers.zig b/exercises/practice/complex-numbers/complex_numbers.zig new file mode 100644 index 00000000..0524d5bd --- /dev/null +++ b/exercises/practice/complex-numbers/complex_numbers.zig @@ -0,0 +1,65 @@ +const std = @import("std"); + +/// Returns a complex number type whose real and imaginary parts are of the +/// float type `T`. +pub fn Complex(comptime T: type) type { + return struct { + const Self = @This(); + + real: T, + imag: T, + + /// Initializes a complex number with the given real and imaginary parts. + pub fn init(real: T, imag: T) Self { + _ = real; + _ = imag; + @compileError("please implement the init function"); + } + + /// Returns the sum of two complex numbers. + pub fn add(self: Self, other: Self) Self { + _ = self; + _ = other; + @compileError("please implement the add function"); + } + + /// Returns the difference of two complex numbers. + pub fn sub(self: Self, other: Self) Self { + _ = self; + _ = other; + @compileError("please implement the sub function"); + } + + /// Returns the product of two complex numbers. + pub fn mul(self: Self, other: Self) Self { + _ = self; + _ = other; + @compileError("please implement the mul function"); + } + + /// Returns the quotient of two complex numbers. + pub fn div(self: Self, other: Self) Self { + _ = self; + _ = other; + @compileError("please implement the div function"); + } + + /// Returns the complex conjugate. + pub fn conjugate(self: Self) Self { + _ = self; + @compileError("please implement the conjugate function"); + } + + /// Returns the absolute value (modulus). + pub fn abs(self: Self) T { + _ = self; + @compileError("please implement the abs function"); + } + + /// Returns the complex exponential function of the number. + pub fn exp(self: Self) Self { + _ = self; + @compileError("please implement the exp function"); + } + }; +} diff --git a/exercises/practice/complex-numbers/test_complex_numbers.zig b/exercises/practice/complex-numbers/test_complex_numbers.zig new file mode 100644 index 00000000..ea121e9d --- /dev/null +++ b/exercises/practice/complex-numbers/test_complex_numbers.zig @@ -0,0 +1,233 @@ +const std = @import("std"); +const testing = std.testing; + +const complex_numbers = @import("complex_numbers.zig"); +const Complex = complex_numbers.Complex(f64); + +const tolerance = 1e-12; + +fn expectEqualComplex(expected: Complex, actual: Complex) !void { + try testing.expectApproxEqAbs(expected.real, actual.real, tolerance); + try testing.expectApproxEqAbs(expected.imag, actual.imag, tolerance); +} + +test "Real part-Real part of a purely real number" { + const z = Complex.init(1, 0); + try testing.expectEqual(1, z.real); +} + +test "Real part-Real part of a purely imaginary number" { + const z = Complex.init(0, 1); + try testing.expectEqual(0, z.real); +} + +test "Real part-Real part of a number with real and imaginary part" { + const z = Complex.init(1, 2); + try testing.expectEqual(1, z.real); +} + +test "Imaginary part-Imaginary part of a purely real number" { + const z = Complex.init(1, 0); + try testing.expectEqual(0, z.imag); +} + +test "Imaginary part-Imaginary part of a purely imaginary number" { + const z = Complex.init(0, 1); + try testing.expectEqual(1, z.imag); +} + +test "Imaginary part-Imaginary part of a number with real and imaginary part" { + const z = Complex.init(1, 2); + try testing.expectEqual(2, z.imag); +} + +test "Imaginary unit" { + const z1 = Complex.init(0, 1); + const z2 = Complex.init(0, 1); + try expectEqualComplex(Complex.init(-1, 0), z1.mul(z2)); +} + +test "Arithmetic-Addition-Add purely real numbers" { + const z1 = Complex.init(1, 0); + const z2 = Complex.init(2, 0); + try expectEqualComplex(Complex.init(3, 0), z1.add(z2)); +} + +test "Arithmetic-Addition-Add purely imaginary numbers" { + const z1 = Complex.init(0, 1); + const z2 = Complex.init(0, 2); + try expectEqualComplex(Complex.init(0, 3), z1.add(z2)); +} + +test "Arithmetic-Addition-Add numbers with real and imaginary part" { + const z1 = Complex.init(1, 2); + const z2 = Complex.init(3, 4); + try expectEqualComplex(Complex.init(4, 6), z1.add(z2)); +} + +test "Arithmetic-Subtraction-Subtract purely real numbers" { + const z1 = Complex.init(1, 0); + const z2 = Complex.init(2, 0); + try expectEqualComplex(Complex.init(-1, 0), z1.sub(z2)); +} + +test "Arithmetic-Subtraction-Subtract purely imaginary numbers" { + const z1 = Complex.init(0, 1); + const z2 = Complex.init(0, 2); + try expectEqualComplex(Complex.init(0, -1), z1.sub(z2)); +} + +test "Arithmetic-Subtraction-Subtract numbers with real and imaginary part" { + const z1 = Complex.init(1, 2); + const z2 = Complex.init(3, 4); + try expectEqualComplex(Complex.init(-2, -2), z1.sub(z2)); +} + +test "Arithmetic-Multiplication-Multiply purely real numbers" { + const z1 = Complex.init(1, 0); + const z2 = Complex.init(2, 0); + try expectEqualComplex(Complex.init(2, 0), z1.mul(z2)); +} + +test "Arithmetic-Multiplication-Multiply purely imaginary numbers" { + const z1 = Complex.init(0, 1); + const z2 = Complex.init(0, 2); + try expectEqualComplex(Complex.init(-2, 0), z1.mul(z2)); +} + +test "Arithmetic-Multiplication-Multiply numbers with real and imaginary part" { + const z1 = Complex.init(1, 2); + const z2 = Complex.init(3, 4); + try expectEqualComplex(Complex.init(-5, 10), z1.mul(z2)); +} + +test "Arithmetic-Division-Divide purely real numbers" { + const z1 = Complex.init(1, 0); + const z2 = Complex.init(2, 0); + try expectEqualComplex(Complex.init(0.5, 0), z1.div(z2)); +} + +test "Arithmetic-Division-Divide purely imaginary numbers" { + const z1 = Complex.init(0, 1); + const z2 = Complex.init(0, 2); + try expectEqualComplex(Complex.init(0.5, 0), z1.div(z2)); +} + +test "Arithmetic-Division-Divide numbers with real and imaginary part" { + const z1 = Complex.init(1, 2); + const z2 = Complex.init(3, 4); + try expectEqualComplex(Complex.init(0.44, 0.08), z1.div(z2)); +} + +test "Absolute value-Absolute value of a positive purely real number" { + const z = Complex.init(5, 0); + try testing.expectApproxEqAbs(5, z.abs(), tolerance); +} + +test "Absolute value-Absolute value of a negative purely real number" { + const z = Complex.init(-5, 0); + try testing.expectApproxEqAbs(5, z.abs(), tolerance); +} + +test "Absolute value-Absolute value of a purely imaginary number with positive imaginary part" { + const z = Complex.init(0, 5); + try testing.expectApproxEqAbs(5, z.abs(), tolerance); +} + +test "Absolute value-Absolute value of a purely imaginary number with negative imaginary part" { + const z = Complex.init(0, -5); + try testing.expectApproxEqAbs(5, z.abs(), tolerance); +} + +test "Absolute value-Absolute value of a number with real and imaginary part" { + const z = Complex.init(3, 4); + try testing.expectApproxEqAbs(5, z.abs(), tolerance); +} + +test "Complex conjugate-Conjugate a purely real number" { + const z = Complex.init(5, 0); + try expectEqualComplex(Complex.init(5, 0), z.conjugate()); +} + +test "Complex conjugate-Conjugate a purely imaginary number" { + const z = Complex.init(0, 5); + try expectEqualComplex(Complex.init(0, -5), z.conjugate()); +} + +test "Complex conjugate-Conjugate a number with real and imaginary part" { + const z = Complex.init(1, 1); + try expectEqualComplex(Complex.init(1, -1), z.conjugate()); +} + +test "Complex exponential function-Euler's identity/formula" { + const z = Complex.init(0, std.math.pi); + try expectEqualComplex(Complex.init(-1, 0), z.exp()); +} + +test "Complex exponential function-Exponential of 0" { + const z = Complex.init(0, 0); + try expectEqualComplex(Complex.init(1, 0), z.exp()); +} + +test "Complex exponential function-Exponential of a purely real number" { + const z = Complex.init(1, 0); + try expectEqualComplex(Complex.init(std.math.e, 0), z.exp()); +} + +test "Complex exponential function-Exponential of a number with real and imaginary part" { + const z = Complex.init(@log(2.0), std.math.pi); + try expectEqualComplex(Complex.init(-2, 0), z.exp()); +} + +test "Complex exponential function-Exponential resulting in a number with real and imaginary part" { + const z = Complex.init(@log(2.0) / 2.0, std.math.pi / 4.0); + try expectEqualComplex(Complex.init(1, 1), z.exp()); +} + +test "Operations between real numbers and complex numbers-Add real number to complex number" { + const z1 = Complex.init(1, 2); + const z2 = Complex.init(5, 0); + try expectEqualComplex(Complex.init(6, 2), z1.add(z2)); +} + +test "Operations between real numbers and complex numbers-Add complex number to real number" { + const z1 = Complex.init(5, 0); + const z2 = Complex.init(1, 2); + try expectEqualComplex(Complex.init(6, 2), z1.add(z2)); +} + +test "Operations between real numbers and complex numbers-Subtract real number from complex number" { + const z1 = Complex.init(5, 7); + const z2 = Complex.init(4, 0); + try expectEqualComplex(Complex.init(1, 7), z1.sub(z2)); +} + +test "Operations between real numbers and complex numbers-Subtract complex number from real number" { + const z1 = Complex.init(4, 0); + const z2 = Complex.init(5, 7); + try expectEqualComplex(Complex.init(-1, -7), z1.sub(z2)); +} + +test "Operations between real numbers and complex numbers-Multiply complex number by real number" { + const z1 = Complex.init(2, 5); + const z2 = Complex.init(5, 0); + try expectEqualComplex(Complex.init(10, 25), z1.mul(z2)); +} + +test "Operations between real numbers and complex numbers-Multiply real number by complex number" { + const z1 = Complex.init(5, 0); + const z2 = Complex.init(2, 5); + try expectEqualComplex(Complex.init(10, 25), z1.mul(z2)); +} + +test "Operations between real numbers and complex numbers-Divide complex number by real number" { + const z1 = Complex.init(10, 100); + const z2 = Complex.init(10, 0); + try expectEqualComplex(Complex.init(1, 10), z1.div(z2)); +} + +test "Operations between real numbers and complex numbers-Divide real number by complex number" { + const z1 = Complex.init(5, 0); + const z2 = Complex.init(1, 1); + try expectEqualComplex(Complex.init(2.5, -2.5), z1.div(z2)); +} diff --git a/generators/exercises/complex_numbers.py b/generators/exercises/complex_numbers.py new file mode 100644 index 00000000..b883e0a4 --- /dev/null +++ b/generators/exercises/complex_numbers.py @@ -0,0 +1,63 @@ +IMPORT_SELF = True + +HEADER = """const Complex = complex_numbers.Complex(f64); + +const tolerance = 1e-12; + +fn expectEqualComplex(expected: Complex, actual: Complex) !void { + try testing.expectApproxEqAbs(expected.real, actual.real, tolerance); + try testing.expectApproxEqAbs(expected.imag, actual.imag, tolerance); +} +""" + +# Symbolic values appearing in the canonical data. +EXPRESSIONS = { + "pi": "std.math.pi", + "e": "std.math.e", + "ln(2)": "@log(2.0)", + "ln(2)/2": "@log(2.0) / 2.0", + "pi/4": "std.math.pi / 4.0", +} + + +def scalar(v): + return EXPRESSIONS[v] if isinstance(v, str) else repr(v) + + +def complex_(v): + # A bare number stands for a complex number with imaginary part 0. + if not isinstance(v, list): + v = [v, 0] + return f"Complex.init({scalar(v[0])}, {scalar(v[1])})" + + +def gen_case(case): + prop = case["property"] + inp = case["input"] + e = case["expected"] + + if prop in ("real", "imaginary"): + field = "real" if prop == "real" else "imag" + return ( + f" const z = {complex_(inp['z'])};\n" + f" try testing.expectEqual({scalar(e)}, z.{field});\n" + ) + + if prop == "abs": + return ( + f" const z = {complex_(inp['z'])};\n" + f" try testing.expectApproxEqAbs({scalar(e)}, z.abs(), tolerance);\n" + ) + + if prop in ("conjugate", "exp"): + return ( + f" const z = {complex_(inp['z'])};\n" + f" try expectEqualComplex({complex_(e)}, z.{prop}());\n" + ) + + # Binary operations: add, sub, mul, div. + return ( + f" const z1 = {complex_(inp['z1'])};\n" + f" const z2 = {complex_(inp['z2'])};\n" + f" try expectEqualComplex({complex_(e)}, z1.{prop}(z2));\n" + )