Skip to main content

ocas_domain/
complex.rs

1//! Complex number domain implementation.
2//!
3//! This module provides a generic complex domain built on top of any base
4//! [`Domain`]. The default build uses [`num_complex::Complex`] as the storage
5//! representation; a future MPC backend may be added behind a feature flag.
6
7use std::marker::PhantomData;
8
9use num_complex::Complex as NumComplex;
10
11use crate::domain::Domain;
12
13/// A complex number whose real and imaginary parts live in a base domain.
14#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15pub struct Complex<D: Domain> {
16    inner: NumComplex<D::Element>,
17}
18
19impl<D: Domain> Complex<D> {
20    /// Create a complex number from real and imaginary parts.
21    pub fn new(real: D::Element, imag: D::Element) -> Self {
22        Self {
23            inner: NumComplex::new(real, imag),
24        }
25    }
26
27    /// Access the real part.
28    pub fn re(&self) -> &D::Element {
29        &self.inner.re
30    }
31
32    /// Access the imaginary part.
33    pub fn im(&self) -> &D::Element {
34        &self.inner.im
35    }
36
37    /// Access the underlying `num_complex::Complex`.
38    pub fn inner(&self) -> &NumComplex<D::Element> {
39        &self.inner
40    }
41}
42
43/// The complex domain over a base domain `D`.
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub struct ComplexDomain<D: Domain> {
46    base: D,
47    _marker: PhantomData<D>,
48}
49
50impl<D: Domain> ComplexDomain<D> {
51    /// Create the complex domain over `base`.
52    pub fn new(base: D) -> Self {
53        Self {
54            base,
55            _marker: PhantomData,
56        }
57    }
58
59    /// Return a reference to the base domain.
60    pub fn base(&self) -> &D {
61        &self.base
62    }
63
64    /// Create a purely real element in this domain.
65    pub fn real_element(&self, re: D::Element) -> Complex<D> {
66        Complex::new(re, self.base.zero())
67    }
68
69    /// Create a purely imaginary element in this domain.
70    pub fn imag_element(&self, im: D::Element) -> Complex<D> {
71        Complex::new(self.base.zero(), im)
72    }
73}
74
75impl<D: Domain> Domain for ComplexDomain<D> {
76    type Element = Complex<D>;
77
78    fn zero(&self) -> Self::Element {
79        Complex::new(self.base.zero(), self.base.zero())
80    }
81
82    fn one(&self) -> Self::Element {
83        Complex::new(self.base.one(), self.base.zero())
84    }
85
86    fn add(&self, a: &Self::Element, b: &Self::Element) -> Self::Element {
87        Complex::new(self.base.add(a.re(), b.re()), self.base.add(a.im(), b.im()))
88    }
89
90    fn sub(&self, a: &Self::Element, b: &Self::Element) -> Self::Element {
91        Complex::new(self.base.sub(a.re(), b.re()), self.base.sub(a.im(), b.im()))
92    }
93
94    fn neg(&self, a: &Self::Element) -> Self::Element {
95        Complex::new(self.base.neg(a.re()), self.base.neg(a.im()))
96    }
97
98    fn mul(&self, a: &Self::Element, b: &Self::Element) -> Self::Element {
99        // (a + bi)(c + di) = (ac - bd) + (ad + bc)i
100        let ac = self.base.mul(a.re(), b.re());
101        let bd = self.base.mul(a.im(), b.im());
102        let ad = self.base.mul(a.re(), b.im());
103        let bc = self.base.mul(a.im(), b.re());
104        Complex::new(self.base.sub(&ac, &bd), self.base.add(&ad, &bc))
105    }
106
107    fn div(&self, a: &Self::Element, b: &Self::Element) -> Option<Self::Element> {
108        // (a + bi)/(c + di) = ((ac + bd) + (bc - ad)i) / (c^2 + d^2)
109        let c2 = self.base.mul(b.re(), b.re());
110        let d2 = self.base.mul(b.im(), b.im());
111        let denom = self.base.add(&c2, &d2);
112        let ac = self.base.mul(a.re(), b.re());
113        let bd = self.base.mul(a.im(), b.im());
114        let bc = self.base.mul(a.im(), b.re());
115        let ad = self.base.mul(a.re(), b.im());
116        let real_num = self.base.add(&ac, &bd);
117        let imag_num = self.base.sub(&bc, &ad);
118        let real = self.base.div(&real_num, &denom)?;
119        let imag = self.base.div(&imag_num, &denom)?;
120        Some(Complex::new(real, imag))
121    }
122
123    fn inv(&self, a: &Self::Element) -> Option<Self::Element> {
124        self.div(&self.one(), a)
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131    use crate::{IntegerDomain, Rational, RationalDomain};
132
133    #[test]
134    fn complex_addition() {
135        let base = IntegerDomain;
136        let domain = ComplexDomain::new(base);
137        let a = Complex::new(1.into(), 2.into());
138        let b = Complex::new(3.into(), 4.into());
139        let sum = domain.add(&a, &b);
140        assert_eq!(sum.re(), &4.into());
141        assert_eq!(sum.im(), &6.into());
142    }
143
144    #[test]
145    fn complex_multiplication() {
146        let base = IntegerDomain;
147        let domain = ComplexDomain::new(base);
148        let a = Complex::new(1.into(), 2.into());
149        let b = Complex::new(3.into(), 4.into());
150        let prod = domain.mul(&a, &b);
151        // (1 + 2i)(3 + 4i) = -5 + 10i
152        assert_eq!(prod.re(), &(-5).into());
153        assert_eq!(prod.im(), &10.into());
154    }
155
156    #[test]
157    fn complex_i_squared_is_minus_one() {
158        let base = IntegerDomain;
159        let domain = ComplexDomain::new(base);
160        let i = domain.imag_element(1.into());
161        let i2 = domain.mul(&i, &i);
162        assert_eq!(i2.re(), &(-1).into());
163        assert_eq!(i2.im(), &0.into());
164    }
165
166    #[test]
167    fn complex_inverse_over_rationals() {
168        let base = RationalDomain;
169        let domain = ComplexDomain::new(base);
170        let z = Complex::new(Rational::new(1, 1), Rational::new(1, 1));
171        let inv = domain.inv(&z).expect("non-zero complex is invertible");
172        let prod = domain.mul(&z, &inv);
173        assert_eq!(prod, domain.one());
174    }
175}