Coverage Report

Created: 2025-04-11 06:16

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