Skip to main content

oxilean_std/complex/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::complex_type::Complex;
6use super::functions::*;
7
8/// A Möbius transformation f(z) = (az + b) / (cz + d) with ad - bc ≠ 0.
9#[derive(Clone, Copy, Debug)]
10pub struct MobiusTransform {
11    /// Numerator coefficient of z.
12    pub a: Complex,
13    /// Numerator constant.
14    pub b: Complex,
15    /// Denominator coefficient of z.
16    pub c: Complex,
17    /// Denominator constant.
18    pub d: Complex,
19}
20impl MobiusTransform {
21    /// Create a new Möbius transformation. Returns `None` if det = ad - bc = 0.
22    pub fn new(a: Complex, b: Complex, c: Complex, d: Complex) -> Option<Self> {
23        let det = a.mul(d).sub(b.mul(c));
24        if det.abs() < f64::EPSILON {
25            return None;
26        }
27        Some(Self { a, b, c, d })
28    }
29    /// Identity transformation z ↦ z.
30    pub fn identity() -> Self {
31        Self {
32            a: Complex::one(),
33            b: Complex::zero(),
34            c: Complex::zero(),
35            d: Complex::one(),
36        }
37    }
38    /// Apply the transformation to z. Returns `None` at the pole cz + d = 0.
39    pub fn apply(self, z: Complex) -> Option<Complex> {
40        let numer = self.a.mul(z).add(self.b);
41        let denom = self.c.mul(z).add(self.d);
42        numer.div(denom)
43    }
44    /// Compose two Möbius transformations: (self ∘ other)(z) = self(other(z)).
45    pub fn compose(self, other: Self) -> Option<Self> {
46        let a = self.a.mul(other.a).add(self.b.mul(other.c));
47        let b = self.a.mul(other.b).add(self.b.mul(other.d));
48        let c = self.c.mul(other.a).add(self.d.mul(other.c));
49        let d = self.c.mul(other.b).add(self.d.mul(other.d));
50        Self::new(a, b, c, d)
51    }
52    /// Invert the transformation: f^{-1}(z) = (dz - b) / (-cz + a).
53    pub fn invert(self) -> Option<Self> {
54        Self::new(self.d, self.b.neg(), self.c.neg(), self.a)
55    }
56    /// Fixed points of the transformation: solutions to f(z) = z.
57    /// Solves cz² + (d - a)z - b = 0.
58    pub fn fixed_points(self) -> Vec<Complex> {
59        if self.c.abs() < f64::EPSILON {
60            let da = self.d.sub(self.a);
61            if da.abs() < f64::EPSILON {
62                return vec![];
63            }
64            return self.b.div(da).map(|z| vec![z]).unwrap_or_default();
65        }
66        let p = self.d.sub(self.a);
67        let disc = p.mul(p).add(self.b.mul(self.c).scale(4.0));
68        let disc_sqrt = disc.sqrt();
69        let two_c = self.c.scale(2.0);
70        let mut pts = vec![];
71        if let Some(z1) = p.neg().add(disc_sqrt).div(two_c) {
72            pts.push(z1);
73        }
74        if let Some(z2) = p.neg().sub(disc_sqrt).div(two_c) {
75            if pts.is_empty() || !z2.approx_eq(pts[0], 1e-12) {
76                pts.push(z2);
77            }
78        }
79        pts
80    }
81}
82/// Conformal mapping: Riemann mapping theorem (abstract).
83#[allow(dead_code)]
84#[derive(Debug, Clone)]
85pub struct ConformalMap {
86    pub source_domain: String,
87    pub target_domain: String,
88    pub description: String,
89}
90#[allow(dead_code)]
91impl ConformalMap {
92    pub fn new(source: &str, target: &str, desc: &str) -> Self {
93        ConformalMap {
94            source_domain: source.to_string(),
95            target_domain: target.to_string(),
96            description: desc.to_string(),
97        }
98    }
99    pub fn riemann_mapping(domain: &str) -> Self {
100        ConformalMap::new(domain, "UnitDisk", "Riemann mapping theorem")
101    }
102    pub fn upper_half_to_disk() -> Self {
103        ConformalMap::new("UpperHalfPlane", "UnitDisk", "z → (z-i)/(z+i)")
104    }
105    pub fn joukowski() -> Self {
106        ConformalMap::new("UnitDisk", "Airfoil", "z → z + 1/z")
107    }
108    pub fn is_biholomorphic(&self) -> bool {
109        true
110    }
111}
112/// Möbius transformation: f(z) = (az+b)/(cz+d).
113#[allow(dead_code)]
114#[derive(Debug, Clone, Copy)]
115pub struct MobiusMap {
116    pub a: C64,
117    pub b: C64,
118    pub c: C64,
119    pub d: C64,
120}
121#[allow(dead_code)]
122impl MobiusMap {
123    pub fn new(a: C64, b: C64, c: C64, d: C64) -> Self {
124        MobiusMap { a, b, c, d }
125    }
126    pub fn identity() -> Self {
127        MobiusMap {
128            a: C64::one(),
129            b: C64::zero(),
130            c: C64::zero(),
131            d: C64::one(),
132        }
133    }
134    pub fn determinant(&self) -> C64 {
135        self.a.mul(&self.d).sub(&self.b.mul(&self.c))
136    }
137    pub fn apply(&self, z: C64) -> Option<C64> {
138        let num = self.a.mul(&z).add(&self.b);
139        let den = self.c.mul(&z).add(&self.d);
140        num.div(&den)
141    }
142    pub fn compose(&self, other: &MobiusMap) -> MobiusMap {
143        MobiusMap {
144            a: self.a.mul(&other.a).add(&self.b.mul(&other.c)),
145            b: self.a.mul(&other.b).add(&self.b.mul(&other.d)),
146            c: self.c.mul(&other.a).add(&self.d.mul(&other.c)),
147            d: self.c.mul(&other.b).add(&self.d.mul(&other.d)),
148        }
149    }
150    pub fn is_parabolic(&self) -> bool {
151        let tr = self.a.add(&self.d);
152        let tr2 = tr.mul(&tr);
153        (tr2.re - 4.0).abs() < 1e-9 && tr2.im.abs() < 1e-9
154    }
155}
156/// Complex number (64-bit floating point).
157#[allow(dead_code)]
158#[derive(Debug, Clone, Copy, PartialEq)]
159pub struct C64 {
160    pub re: f64,
161    pub im: f64,
162}
163#[allow(dead_code)]
164impl C64 {
165    pub fn new(re: f64, im: f64) -> Self {
166        C64 { re, im }
167    }
168    pub fn zero() -> Self {
169        C64::new(0.0, 0.0)
170    }
171    pub fn one() -> Self {
172        C64::new(1.0, 0.0)
173    }
174    pub fn i() -> Self {
175        C64::new(0.0, 1.0)
176    }
177    pub fn from_polar(r: f64, theta: f64) -> Self {
178        C64::new(r * theta.cos(), r * theta.sin())
179    }
180    pub fn modulus(&self) -> f64 {
181        (self.re * self.re + self.im * self.im).sqrt()
182    }
183    pub fn argument(&self) -> f64 {
184        self.im.atan2(self.re)
185    }
186    pub fn conjugate(&self) -> Self {
187        C64::new(self.re, -self.im)
188    }
189    pub fn add(&self, other: &C64) -> C64 {
190        C64::new(self.re + other.re, self.im + other.im)
191    }
192    pub fn sub(&self, other: &C64) -> C64 {
193        C64::new(self.re - other.re, self.im - other.im)
194    }
195    pub fn mul(&self, other: &C64) -> C64 {
196        C64::new(
197            self.re * other.re - self.im * other.im,
198            self.re * other.im + self.im * other.re,
199        )
200    }
201    pub fn div(&self, other: &C64) -> Option<C64> {
202        let denom = other.re * other.re + other.im * other.im;
203        if denom.abs() < 1e-15 {
204            return None;
205        }
206        Some(C64::new(
207            (self.re * other.re + self.im * other.im) / denom,
208            (self.im * other.re - self.re * other.im) / denom,
209        ))
210    }
211    pub fn exp(&self) -> C64 {
212        let r = self.re.exp();
213        C64::new(r * self.im.cos(), r * self.im.sin())
214    }
215    pub fn log(&self) -> Option<C64> {
216        let r = self.modulus();
217        if r < 1e-15 {
218            return None;
219        }
220        Some(C64::new(r.ln(), self.argument()))
221    }
222    pub fn pow_n(&self, n: i32) -> C64 {
223        let r = self.modulus().powi(n);
224        let theta = self.argument() * n as f64;
225        C64::from_polar(r, theta)
226    }
227    pub fn sqrt_principal(&self) -> C64 {
228        let r = self.modulus().sqrt();
229        let theta = self.argument() / 2.0;
230        C64::from_polar(r, theta)
231    }
232    pub fn sin(&self) -> C64 {
233        C64::new(
234            self.re.sin() * self.im.cosh(),
235            self.re.cos() * self.im.sinh(),
236        )
237    }
238    pub fn cos(&self) -> C64 {
239        C64::new(
240            self.re.cos() * self.im.cosh(),
241            -self.re.sin() * self.im.sinh(),
242        )
243    }
244}