Coverage Report

Created: 2025-07-01 06:59

/src/igraph/src/math/complex.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   IGraph library.
3
   Copyright (C) 2010-2012  Gabor Csardi <csardi.gabor@gmail.com>
4
   334 Harvard street, Cambridge, MA 02139 USA
5
6
   This program is free software; you can redistribute it and/or modify
7
   it under the terms of the GNU General Public License as published by
8
   the Free Software Foundation; either version 2 of the License, or
9
   (at your option) any later version.
10
11
   This program is distributed in the hope that it will be useful,
12
   but WITHOUT ANY WARRANTY; without even the implied warranty of
13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
   GNU General Public License for more details.
15
16
   You should have received a copy of the GNU General Public License
17
   along with this program; if not, write to the Free Software
18
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
   02110-1301 USA
20
21
*/
22
23
#include "igraph_complex.h"
24
25
#include <math.h>
26
27
/**
28
 * \example igraph_complex.c
29
 */
30
31
0
igraph_complex_t igraph_complex(igraph_real_t x, igraph_real_t y) {
32
0
    igraph_complex_t res;
33
0
    IGRAPH_REAL(res) = x;
34
0
    IGRAPH_IMAG(res) = y;
35
0
    return res;
36
0
}
37
38
0
igraph_complex_t igraph_complex_polar(igraph_real_t r, igraph_real_t theta) {
39
0
    igraph_complex_t res;
40
0
    IGRAPH_REAL(res) = r * cos(theta);
41
0
    IGRAPH_IMAG(res) = r * sin(theta);
42
0
    return res;
43
0
}
44
45
0
igraph_real_t igraph_complex_arg(igraph_complex_t z) {
46
0
    igraph_real_t x = IGRAPH_REAL(z);
47
0
    igraph_real_t y = IGRAPH_IMAG(z);
48
0
    if (x == 0.0 && y == 0.0) {
49
0
        return 0.0;
50
0
    }
51
0
    return atan2(y, x);
52
0
}
53
54
igraph_complex_t igraph_complex_add(igraph_complex_t z1,
55
0
                                    igraph_complex_t z2) {
56
0
    igraph_complex_t res;
57
0
    IGRAPH_REAL(res) = IGRAPH_REAL(z1) + IGRAPH_REAL(z2);
58
0
    IGRAPH_IMAG(res) = IGRAPH_IMAG(z1) + IGRAPH_IMAG(z2);
59
0
    return res;
60
0
}
61
62
igraph_complex_t igraph_complex_sub(igraph_complex_t z1,
63
0
                                    igraph_complex_t z2) {
64
0
    igraph_complex_t res;
65
0
    IGRAPH_REAL(res) = IGRAPH_REAL(z1) - IGRAPH_REAL(z2);
66
0
    IGRAPH_IMAG(res) = IGRAPH_IMAG(z1) - IGRAPH_IMAG(z2);
67
0
    return res;
68
0
}
69
70
igraph_complex_t igraph_complex_mul(igraph_complex_t z1,
71
0
                                    igraph_complex_t z2) {
72
0
    igraph_complex_t res;
73
0
    IGRAPH_REAL(res) = IGRAPH_REAL(z1) * IGRAPH_REAL(z2) -
74
0
                       IGRAPH_IMAG(z1) * IGRAPH_IMAG(z2);
75
0
    IGRAPH_IMAG(res) = IGRAPH_REAL(z1) * IGRAPH_IMAG(z2) +
76
0
                       IGRAPH_IMAG(z1) * IGRAPH_REAL(z2);
77
0
    return res;
78
0
}
79
80
igraph_complex_t igraph_complex_div(igraph_complex_t z1,
81
0
                                    igraph_complex_t z2) {
82
0
    igraph_complex_t res;
83
0
    igraph_real_t z1r = IGRAPH_REAL(z1), z1i = IGRAPH_IMAG(z1);
84
0
    igraph_real_t z2r = IGRAPH_REAL(z2), z2i = IGRAPH_IMAG(z2);
85
0
    igraph_real_t s = 1.0 / igraph_complex_abs(z2);
86
0
    igraph_real_t sz2r = s * z2r;
87
0
    igraph_real_t sz2i = s * z2i;
88
0
    IGRAPH_REAL(res) = (z1r * sz2r + z1i * sz2i) * s;
89
0
    IGRAPH_IMAG(res) = (z1i * sz2r - z1r * sz2i) * s;
90
0
    return res;
91
0
}
92
93
igraph_complex_t igraph_complex_add_real(igraph_complex_t z,
94
0
        igraph_real_t x) {
95
0
    igraph_complex_t res;
96
0
    IGRAPH_REAL(res) = IGRAPH_REAL(z) + x;
97
0
    IGRAPH_IMAG(res) = IGRAPH_IMAG(z);
98
0
    return res;
99
0
}
100
101
igraph_complex_t igraph_complex_add_imag(igraph_complex_t z,
102
0
        igraph_real_t y) {
103
0
    igraph_complex_t res;
104
0
    IGRAPH_REAL(res) = IGRAPH_REAL(z);
105
0
    IGRAPH_IMAG(res) = IGRAPH_IMAG(z) + y;
106
0
    return res;
107
0
}
108
109
igraph_complex_t igraph_complex_sub_real(igraph_complex_t z,
110
0
        igraph_real_t x) {
111
0
    igraph_complex_t res;
112
0
    IGRAPH_REAL(res) = IGRAPH_REAL(z) - x;
113
0
    IGRAPH_IMAG(res) = IGRAPH_IMAG(z);
114
0
    return res;
115
0
}
116
117
igraph_complex_t igraph_complex_sub_imag(igraph_complex_t z,
118
0
        igraph_real_t y) {
119
0
    igraph_complex_t res;
120
0
    IGRAPH_REAL(res) = IGRAPH_REAL(z);
121
0
    IGRAPH_IMAG(res) = IGRAPH_IMAG(z) - y;
122
0
    return res;
123
0
}
124
125
igraph_complex_t igraph_complex_mul_real(igraph_complex_t z,
126
0
        igraph_real_t x) {
127
0
    igraph_complex_t res;
128
0
    IGRAPH_REAL(res) = IGRAPH_REAL(z) * x;
129
0
    IGRAPH_IMAG(res) = IGRAPH_IMAG(z) * x;
130
0
    return res;
131
0
}
132
133
igraph_complex_t igraph_complex_mul_imag(igraph_complex_t z,
134
0
        igraph_real_t y) {
135
0
    igraph_complex_t res;
136
0
    IGRAPH_REAL(res) = - IGRAPH_IMAG(z) * y;
137
0
    IGRAPH_IMAG(res) =   IGRAPH_REAL(z) * y;
138
0
    return res;
139
0
}
140
141
igraph_complex_t igraph_complex_div_real(igraph_complex_t z,
142
0
        igraph_real_t x) {
143
0
    igraph_complex_t res;
144
0
    IGRAPH_REAL(res) = IGRAPH_REAL(z) / x;
145
0
    IGRAPH_IMAG(res) = IGRAPH_IMAG(z) / x;
146
0
    return res;
147
0
}
148
149
igraph_complex_t igraph_complex_div_imag(igraph_complex_t z,
150
0
        igraph_real_t y) {
151
0
    igraph_complex_t res;
152
0
    IGRAPH_REAL(res) =   IGRAPH_IMAG(z) / y;
153
0
    IGRAPH_IMAG(res) = - IGRAPH_REAL(z) / y;
154
0
    return res;
155
0
}
156
157
0
igraph_complex_t igraph_complex_conj(igraph_complex_t z) {
158
0
    igraph_complex_t res;
159
0
    IGRAPH_REAL(res) =   IGRAPH_REAL(z);
160
0
    IGRAPH_IMAG(res) = - IGRAPH_IMAG(z);
161
0
    return res;
162
0
}
163
164
0
igraph_complex_t igraph_complex_neg(igraph_complex_t z) {
165
0
    igraph_complex_t res;
166
0
    IGRAPH_REAL(res) = - IGRAPH_REAL(z);
167
0
    IGRAPH_IMAG(res) = - IGRAPH_IMAG(z);
168
0
    return res;
169
0
}
170
171
0
igraph_complex_t igraph_complex_inv(igraph_complex_t z) {
172
0
    igraph_complex_t res;
173
0
    igraph_real_t s = 1.0 / igraph_complex_abs(z);
174
0
    IGRAPH_REAL(res) =   (IGRAPH_REAL(z) * s) * s;
175
0
    IGRAPH_IMAG(res) = - (IGRAPH_IMAG(z) * s) * s;
176
0
    return res;
177
0
}
178
179
0
igraph_real_t igraph_complex_abs(igraph_complex_t z) {
180
    /* hypot() avoids overflow at intermediate stages of the calculation */
181
0
    return hypot(IGRAPH_REAL(z), IGRAPH_IMAG(z));
182
0
}
183
184
0
igraph_real_t igraph_complex_logabs(igraph_complex_t z) {
185
0
    igraph_real_t xabs = fabs(IGRAPH_REAL(z));
186
0
    igraph_real_t yabs = fabs(IGRAPH_IMAG(z));
187
0
    igraph_real_t max, u;
188
0
    if (xabs >= yabs) {
189
0
        max = xabs;
190
0
        u = yabs / xabs;
191
0
    } else {
192
0
        max = yabs;
193
0
        u = xabs / yabs;
194
0
    }
195
0
    return log (max) + 0.5 * log1p (u * u);
196
0
}
197
198
0
igraph_complex_t igraph_complex_sqrt(igraph_complex_t z) {
199
0
    igraph_complex_t res;
200
201
0
    if (IGRAPH_REAL(z) == 0.0 && IGRAPH_IMAG(z) == 0.0) {
202
0
        IGRAPH_REAL(res) = IGRAPH_IMAG(res) = 0.0;
203
0
    } else {
204
0
        igraph_real_t x = fabs (IGRAPH_REAL(z));
205
0
        igraph_real_t y = fabs (IGRAPH_IMAG(z));
206
0
        igraph_real_t w;
207
0
        if (x >= y)  {
208
0
            igraph_real_t t = y / x;
209
0
            w = sqrt (x) * sqrt (0.5 * (1.0 + sqrt (1.0 + t * t)));
210
0
        } else {
211
0
            igraph_real_t t = x / y;
212
0
            w = sqrt (y) * sqrt (0.5 * (t + sqrt (1.0 + t * t)));
213
0
        }
214
215
0
        if (IGRAPH_REAL(z) >= 0.0) {
216
0
            igraph_real_t ai = IGRAPH_IMAG(z);
217
0
            IGRAPH_REAL(res) = w;
218
0
            IGRAPH_IMAG(res) = ai / (2.0 * w);
219
0
        } else {
220
0
            igraph_real_t ai = IGRAPH_IMAG(z);
221
0
            igraph_real_t vi = (ai >= 0) ? w : -w;
222
0
            IGRAPH_REAL(res) = ai / (2.0 * vi);
223
0
            IGRAPH_IMAG(res) = vi;
224
0
        }
225
0
    }
226
227
0
    return res;
228
0
}
229
230
0
igraph_complex_t igraph_complex_sqrt_real(igraph_real_t x) {
231
0
    igraph_complex_t res;
232
0
    if (x >= 0) {
233
0
        IGRAPH_REAL(res) = sqrt(x);
234
0
        IGRAPH_IMAG(res) = 0.0;
235
0
    } else {
236
0
        IGRAPH_REAL(res) = 0.0;
237
0
        IGRAPH_IMAG(res) = sqrt(-x);
238
0
    }
239
0
    return res;
240
0
}
241
242
0
igraph_complex_t igraph_complex_exp(igraph_complex_t z) {
243
0
    igraph_real_t rho   = exp(IGRAPH_REAL(z));
244
0
    igraph_real_t theta = IGRAPH_IMAG(z);
245
0
    igraph_complex_t res;
246
0
    IGRAPH_REAL(res) = rho * cos(theta);
247
0
    IGRAPH_IMAG(res) = rho * sin(theta);
248
0
    return res;
249
0
}
250
251
igraph_complex_t igraph_complex_pow(igraph_complex_t z1,
252
0
                                    igraph_complex_t z2) {
253
0
    igraph_complex_t res;
254
255
0
    if (IGRAPH_REAL(z1) == 0 && IGRAPH_IMAG(z1) == 0.0) {
256
0
        if (IGRAPH_REAL(z2) == 0 && IGRAPH_IMAG(z2) == 0.0) {
257
0
            IGRAPH_REAL(res) = 1.0;
258
0
            IGRAPH_IMAG(res) = 0.0;
259
0
        } else {
260
0
            IGRAPH_REAL(res) = IGRAPH_IMAG(res) = 0.0;
261
0
        }
262
0
    } else if (IGRAPH_REAL(z2) == 1.0 && IGRAPH_IMAG(z2) == 0.0) {
263
0
        IGRAPH_REAL(res) = IGRAPH_REAL(z1);
264
0
        IGRAPH_IMAG(res) = IGRAPH_IMAG(z1);
265
0
    } else if (IGRAPH_REAL(z2) == -1.0 && IGRAPH_IMAG(z2) == 0.0) {
266
0
        res = igraph_complex_inv(z1);
267
0
    } else {
268
0
        igraph_real_t logr = igraph_complex_logabs (z1);
269
0
        igraph_real_t theta = igraph_complex_arg (z1);
270
0
        igraph_real_t z2r = IGRAPH_REAL(z2), z2i = IGRAPH_IMAG(z2);
271
0
        igraph_real_t rho = exp (logr * z2r - z2i * theta);
272
0
        igraph_real_t beta = theta * z2r + z2i * logr;
273
0
        IGRAPH_REAL(res) = rho * cos(beta);
274
0
        IGRAPH_IMAG(res) = rho * sin(beta);
275
0
    }
276
277
0
    return res;
278
0
}
279
280
igraph_complex_t igraph_complex_pow_real(igraph_complex_t z,
281
0
        igraph_real_t x) {
282
0
    igraph_complex_t res;
283
0
    if (IGRAPH_REAL(z) == 0.0 && IGRAPH_IMAG(z) == 0.0) {
284
0
        if (x == 0) {
285
0
            IGRAPH_REAL(res) = 1.0;
286
0
            IGRAPH_IMAG(res) = 0.0;
287
0
        } else {
288
0
            IGRAPH_REAL(res) = IGRAPH_IMAG(res) = 0.0;
289
0
        }
290
0
    } else {
291
0
        igraph_real_t logr = igraph_complex_logabs(z);
292
0
        igraph_real_t theta = igraph_complex_arg(z);
293
0
        igraph_real_t rho = exp (logr * x);
294
0
        igraph_real_t beta = theta * x;
295
0
        IGRAPH_REAL(res) = rho * cos(beta);
296
0
        IGRAPH_IMAG(res) = rho * sin(beta);
297
0
    }
298
0
    return res;
299
0
}
300
301
0
igraph_complex_t igraph_complex_log(igraph_complex_t z) {
302
0
    igraph_complex_t res;
303
0
    IGRAPH_REAL(res) = igraph_complex_logabs(z);
304
0
    IGRAPH_IMAG(res) = igraph_complex_arg(z);
305
0
    return res;
306
0
}
307
308
0
igraph_complex_t igraph_complex_log10(igraph_complex_t z) {
309
0
    return igraph_complex_mul_real(igraph_complex_log(z), 1 / log(10.0));
310
0
}
311
312
igraph_complex_t igraph_complex_log_b(igraph_complex_t z,
313
0
                                      igraph_complex_t b) {
314
0
    return igraph_complex_div (igraph_complex_log(z), igraph_complex_log(b));
315
0
}
316
317
0
igraph_complex_t igraph_complex_sin(igraph_complex_t z) {
318
0
    igraph_real_t zr = IGRAPH_REAL(z);
319
0
    igraph_real_t zi = IGRAPH_IMAG(z);
320
0
    igraph_complex_t res;
321
0
    if (zi == 0.0) {
322
0
        IGRAPH_REAL(res) = sin(zr);
323
0
        IGRAPH_IMAG(res) = 0.0;
324
0
    } else {
325
0
        IGRAPH_REAL(res) = sin(zr) * cosh(zi);
326
0
        IGRAPH_IMAG(res) = cos(zr) * sinh(zi);
327
0
    }
328
0
    return res;
329
0
}
330
331
0
igraph_complex_t igraph_complex_cos(igraph_complex_t z) {
332
0
    igraph_real_t zr = IGRAPH_REAL(z);
333
0
    igraph_real_t zi = IGRAPH_IMAG(z);
334
0
    igraph_complex_t res;
335
0
    if (zi == 0.0) {
336
0
        IGRAPH_REAL(res) = cos(zr);
337
0
        IGRAPH_IMAG(res) = 0.0;
338
0
    } else {
339
0
        IGRAPH_REAL(res) = cos(zr) * cosh(zi);
340
0
        IGRAPH_IMAG(res) = sin(zr) * sinh(-zi);
341
0
    }
342
0
    return res;
343
0
}
344
345
0
igraph_complex_t igraph_complex_tan(igraph_complex_t z) {
346
0
    igraph_real_t zr = IGRAPH_REAL(z);
347
0
    igraph_real_t zi = IGRAPH_IMAG(z);
348
0
    igraph_complex_t res;
349
0
    if (fabs (zi) < 1) {
350
0
        igraph_real_t D = pow (cos (zr), 2.0) + pow (sinh (zi), 2.0);
351
0
        IGRAPH_REAL(res) = 0.5 * sin (2 * zr) / D;
352
0
        IGRAPH_IMAG(res) = 0.5 * sinh (2 * zi) / D;
353
0
    } else {
354
0
        igraph_real_t u = exp (-zi);
355
0
        igraph_real_t C = 2 * u / (1 - pow (u, 2.0));
356
0
        igraph_real_t D = 1 + pow (cos (zr), 2.0) * pow (C, 2.0);
357
0
        igraph_real_t S = pow (C, 2.0);
358
0
        igraph_real_t T = 1.0 / tanh (zi);
359
0
        IGRAPH_REAL(res) = 0.5 * sin (2 * zr) * S / D;
360
0
        IGRAPH_IMAG(res) = T / D;
361
0
    }
362
0
    return res;
363
0
}
364
365
0
igraph_complex_t igraph_complex_sec(igraph_complex_t z) {
366
0
    return igraph_complex_inv(igraph_complex_cos(z));
367
0
}
368
369
0
igraph_complex_t igraph_complex_csc(igraph_complex_t z) {
370
0
    return igraph_complex_inv(igraph_complex_sin(z));
371
0
}
372
373
0
igraph_complex_t igraph_complex_cot(igraph_complex_t z) {
374
0
    return igraph_complex_inv(igraph_complex_tan(z));
375
0
}