magnus/r_complex.rs
1use std::fmt;
2
3use rb_sys::{
4 rb_complex_abs, rb_complex_arg, rb_complex_conjugate, rb_complex_imag, rb_complex_new,
5 rb_complex_new_polar, rb_complex_real, ruby_value_type, VALUE,
6};
7
8use crate::{
9 error::{protect, Error},
10 float::Float,
11 into_value::IntoValue,
12 numeric::Numeric,
13 try_convert::TryConvert,
14 value::{
15 private::{self, ReprValue as _},
16 NonZeroValue, ReprValue, Value,
17 },
18 Ruby,
19};
20
21/// A Value pointer to a RComplex struct, Ruby's internal representation of
22/// complex numbers.
23///
24/// See the [`ReprValue`] trait for additional methods available on this type.
25#[derive(Clone, Copy)]
26#[repr(transparent)]
27pub struct RComplex(NonZeroValue);
28
29impl RComplex {
30 /// Return `Some(RComplex)` if `val` is a `RComplex`, `None` otherwise.
31 ///
32 /// # Examples
33 ///
34 /// ```
35 /// use magnus::{eval, RComplex};
36 /// # let _cleanup = unsafe { magnus::embed::init() };
37 ///
38 /// assert!(RComplex::from_value(eval("2+1i").unwrap()).is_some());
39 /// assert!(RComplex::from_value(eval("3").unwrap()).is_none());
40 /// ```
41 #[inline]
42 pub fn from_value(val: Value) -> Option<Self> {
43 unsafe {
44 (val.rb_type() == ruby_value_type::RUBY_T_COMPLEX)
45 .then(|| Self(NonZeroValue::new_unchecked(val)))
46 }
47 }
48
49 #[inline]
50 pub(crate) unsafe fn from_rb_value_unchecked(val: VALUE) -> Self {
51 Self(NonZeroValue::new_unchecked(Value::new(val)))
52 }
53
54 /// Create a new `RComplex`.
55 ///
56 /// # Examples
57 ///
58 /// ```
59 /// use magnus::{Error, RComplex, Ruby};
60 ///
61 /// fn example(ruby: &Ruby) -> Result<(), Error> {
62 /// let complex = RComplex::new(ruby.integer_from_i64(2), ruby.integer_from_i64(1));
63 /// assert_eq!(complex.to_string(), "2+1i");
64 ///
65 /// Ok(())
66 /// }
67 /// # Ruby::init(example).unwrap()
68 /// ```
69 pub fn new<T, U>(real: T, imag: U) -> RComplex
70 where
71 T: Numeric,
72 U: Numeric,
73 {
74 unsafe {
75 RComplex::from_rb_value_unchecked(rb_complex_new(
76 real.as_rb_value(),
77 imag.as_rb_value(),
78 ))
79 }
80 }
81
82 /// Create a new `RComplex` using polar representation.
83 ///
84 /// # Examples
85 ///
86 /// ```
87 /// use magnus::{Error, RComplex, Ruby};
88 ///
89 /// fn example(ruby: &Ruby) -> Result<(), Error> {
90 /// let complex = RComplex::polar(ruby.integer_from_i64(2), ruby.integer_from_i64(3))?;
91 /// assert_eq!(
92 /// complex.to_string(),
93 /// "-1.9799849932008908+0.2822400161197344i"
94 /// );
95 ///
96 /// Ok(())
97 /// }
98 /// # Ruby::init(example).unwrap()
99 /// ```
100 pub fn polar<T, U>(real: T, imag: U) -> Result<RComplex, Error>
101 where
102 T: Numeric,
103 U: Numeric,
104 {
105 protect(|| unsafe {
106 RComplex::from_rb_value_unchecked(rb_complex_new_polar(
107 real.as_rb_value(),
108 imag.as_rb_value(),
109 ))
110 })
111 }
112
113 /// Returns the real part of `self`.
114 ///
115 /// # Examples
116 ///
117 /// ```
118 /// use magnus::{Error, RComplex, Ruby};
119 ///
120 /// fn example(ruby: &Ruby) -> Result<(), Error> {
121 /// let complex = RComplex::new(ruby.integer_from_i64(9), ruby.integer_from_i64(-4));
122 /// assert_eq!(complex.real::<i64>()?, 9);
123 ///
124 /// Ok(())
125 /// }
126 /// # Ruby::init(example).unwrap()
127 /// ```
128 pub fn real<T>(self) -> Result<T, Error>
129 where
130 T: TryConvert,
131 {
132 let val = unsafe { Value::new(rb_complex_real(self.as_rb_value())) };
133 T::try_convert(val)
134 }
135
136 /// Returns the imaginary part of `self`.
137 ///
138 /// # Examples
139 ///
140 /// ```
141 /// use magnus::{Error, RComplex, Ruby};
142 ///
143 /// fn example(ruby: &Ruby) -> Result<(), Error> {
144 /// let complex = RComplex::new(ruby.integer_from_i64(9), ruby.integer_from_i64(-4));
145 /// assert_eq!(complex.imag::<i64>()?, -4);
146 ///
147 /// Ok(())
148 /// }
149 /// # Ruby::init(example).unwrap()
150 /// ```
151 pub fn imag<T>(self) -> Result<T, Error>
152 where
153 T: TryConvert,
154 {
155 let val = unsafe { Value::new(rb_complex_imag(self.as_rb_value())) };
156 T::try_convert(val)
157 }
158
159 /// Returns the complex conjugate.
160 ///
161 /// # Examples
162 ///
163 /// ```
164 /// use magnus::{Error, RComplex, Ruby};
165 ///
166 /// fn example(ruby: &Ruby) -> Result<(), Error> {
167 /// let complex = RComplex::new(ruby.integer_from_i64(1), ruby.integer_from_i64(2));
168 /// assert_eq!(complex.conjugate().to_string(), "1-2i");
169 ///
170 /// Ok(())
171 /// }
172 /// # Ruby::init(example).unwrap()
173 /// ```
174 pub fn conjugate(self) -> Self {
175 unsafe { Self::from_rb_value_unchecked(rb_complex_conjugate(self.as_rb_value())) }
176 }
177
178 /// Returns the absolute (or the magnitude) of `self`.
179 ///
180 /// # Examples
181 ///
182 /// ```
183 /// use magnus::{Error, RComplex, Ruby};
184 ///
185 /// fn example(ruby: &Ruby) -> Result<(), Error> {
186 /// let complex = RComplex::new(ruby.integer_from_i64(3), ruby.integer_from_i64(-4));
187 /// assert_eq!(complex.abs(), 5.0);
188 ///
189 /// Ok(())
190 /// }
191 /// # Ruby::init(example).unwrap()
192 /// ```
193 pub fn abs(self) -> f64 {
194 unsafe { Float::from_rb_value_unchecked(rb_complex_abs(self.as_rb_value())).to_f64() }
195 }
196
197 /// Returns the argument (or the angle) of the polar form of `self`.
198 ///
199 /// # Examples
200 ///
201 /// ```
202 /// use std::f64::consts::PI;
203 ///
204 /// use magnus::{Error, RComplex, Ruby};
205 ///
206 /// fn example(ruby: &Ruby) -> Result<(), Error> {
207 /// let complex = RComplex::polar(ruby.integer_from_i64(3), ruby.float_from_f64(PI / 2.0))?;
208 /// assert_eq!(complex.arg(), 1.5707963267948966);
209 ///
210 /// Ok(())
211 /// }
212 /// # Ruby::init(example).unwrap()
213 /// ```
214 pub fn arg(self) -> f64 {
215 unsafe { Float::from_rb_value_unchecked(rb_complex_arg(self.as_rb_value())).to_f64() }
216 }
217}
218
219impl fmt::Display for RComplex {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 write!(f, "{}", unsafe { self.to_s_infallible() })
222 }
223}
224
225impl fmt::Debug for RComplex {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 write!(f, "{}", self.inspect())
228 }
229}
230
231impl IntoValue for RComplex {
232 #[inline]
233 fn into_value_with(self, _: &Ruby) -> Value {
234 self.0.get()
235 }
236}
237
238unsafe impl private::ReprValue for RComplex {}
239
240impl Numeric for RComplex {}
241
242impl ReprValue for RComplex {}
243
244impl TryConvert for RComplex {
245 fn try_convert(val: Value) -> Result<Self, Error> {
246 Self::from_value(val).ok_or_else(|| {
247 Error::new(
248 Ruby::get_with(val).exception_type_error(),
249 format!("no implicit conversion of {} into Complex", unsafe {
250 val.classname()
251 },),
252 )
253 })
254 }
255}