Mercurial > hg > dmlib
annotate src/dmfft.c @ 2294:7f6ba3b32f54
Cleanups.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Wed, 03 Jul 2019 10:28:43 +0300 |
parents | e68f7b0cb536 |
children | 69a5af2eb1ea |
rev | line source |
---|---|
749 | 1 /* |
2 * Realfftf | |
3 * Originally (C) Copyright 1993 Philib Van Baren | |
4 * Cleaned up and refactored to be re-entrant by | |
5 * Matti 'ccr' Hamalainen <ccr@tnsp.org>, 2013 | |
6 * | |
7 * Note: Output is BIT-REVERSED! so you must use the BitReversed to | |
8 * get legible output, (i.e. Real_i = buffer[ BitReversed[i] ] | |
9 * Imag_i = buffer[ BitReversed[i]+1 ] ) | |
10 * Input is in normal order. | |
11 */ | |
12 #include "dmfft.h" | |
13 #include <math.h> | |
14 | |
15 | |
1691 | 16 #define DM_PW(a, b, c) (sqrt((a) * (a) + (b) * (b)) * (c)) |
17 | |
18 | |
749 | 19 int dmInitializeFFT(DMFFTContext * ctx, const int fftlen) |
20 { | |
21 if (ctx == NULL) | |
22 return DMERR_NULLPTR; | |
23 | |
24 if (fftlen <= 16) | |
25 return DMERR_INVALID_ARGS; | |
26 | |
27 // Allocate necessary tables | |
28 ctx->npoints = fftlen / 2; | |
29 ctx->sinTable = (DMFFTType *) dmMalloc(fftlen * sizeof(DMFFTType)); | |
30 if (ctx->sinTable == NULL) | |
31 return DMERR_MALLOC; | |
32 | |
33 // Bitreversion buffer | |
34 ctx->breversed = (int *) dmMalloc(ctx->npoints * sizeof(int)); | |
35 if (ctx->breversed == NULL) | |
36 { | |
37 dmFree(ctx->sinTable); | |
38 return DMERR_MALLOC; | |
39 } | |
40 | |
41 // Calculate data | |
1691 | 42 for (int i = 0; i < ctx->npoints; i++) |
749 | 43 { |
44 int mask, tmp; | |
45 for (tmp = 0, mask = ctx->npoints / 2; mask > 0; mask >>= 1) | |
46 { | |
47 tmp = (tmp >> 1) + ((i & mask) ? ctx->npoints : 0); | |
48 } | |
49 | |
50 ctx->breversed[i] = tmp; | |
51 } | |
52 | |
1691 | 53 for (int i = 0; i < ctx->npoints; i++) |
749 | 54 { |
751 | 55 ctx->sinTable[ctx->breversed[i]] = -sin((2.0f * DM_PI * i) / fftlen); |
749 | 56 ctx->sinTable[ctx->breversed[i] + 1] = |
751 | 57 -cos((2.0f * DM_PI * i) / fftlen); |
749 | 58 } |
59 | |
60 return DMERR_OK; | |
61 } | |
62 | |
63 | |
64 void dmEndFFT(DMFFTContext * ctx) | |
65 { | |
66 if (ctx != NULL) | |
67 { | |
68 dmFree(ctx->breversed); | |
69 dmFree(ctx->sinTable); | |
1167 | 70 dmMemset(ctx, 0, sizeof(DMFFTContext)); |
749 | 71 } |
72 } | |
73 | |
74 | |
75 int dmRealFFT(DMFFTContext * ctx, DMFFTType * buffer) | |
76 { | |
77 if (ctx == NULL || buffer == NULL) | |
78 return DMERR_NULLPTR; | |
79 | |
1252 | 80 const int *br1 = ctx->breversed + 1; |
81 const int *br2 = ctx->breversed + ctx->npoints - 1; | |
749 | 82 int rounds = ctx->npoints / 2; |
83 DMFFTType *endptr1 = buffer + ctx->npoints * 2; | |
84 | |
85 while (rounds > 0) | |
86 { | |
87 DMFFTType *sptr = ctx->sinTable; | |
88 DMFFTType *A = buffer, | |
89 *B = buffer + rounds * 2; | |
90 | |
91 while (A < endptr1) | |
92 { | |
93 DMFFTType qsin = *sptr; | |
94 DMFFTType qcos = *(sptr + 1); | |
95 DMFFTType *endptr2 = B; | |
96 | |
97 while (A < endptr2) | |
98 { | |
99 DMFFTType v1 = (*B) * qcos + (*(B + 1)) * qsin; | |
100 DMFFTType v2 = (*B) * qsin - (*(B + 1)) * qcos; | |
101 *B = (*A + v1) * 0.5; | |
102 *(A++) = *(B++) - v1; | |
103 *B = (*A - v2) * 0.5; | |
104 *(A++) = *(B++) + v2; | |
105 } | |
106 A = B; | |
107 B += rounds * 2; | |
108 sptr += 2; | |
109 } | |
110 | |
111 rounds >>= 1; | |
112 } | |
113 | |
1252 | 114 // Massage output to get the output for a real input sequence. |
749 | 115 while (br1 <= br2) |
116 { | |
117 const DMFFTType vsin = ctx->sinTable[*br1]; | |
118 const DMFFTType vcos = ctx->sinTable[*br1 + 1]; | |
119 DMFFTType *A = buffer + *br1, | |
120 *B = buffer + *br2, | |
121 HRplus, HRminus, | |
122 HIplus, HIminus, | |
123 tmp1, tmp2; | |
124 | |
125 HRminus = *A - *B; | |
126 HRplus = HRminus + (*B * 2); | |
127 | |
128 HIminus = *(A + 1) - *(B + 1); | |
129 HIplus = HIminus + (*(B + 1) * 2); | |
130 | |
131 tmp1 = (vsin * HRminus) - (vcos * HIplus); | |
132 tmp2 = (vcos * HRminus) + (vsin * HIplus); | |
133 | |
134 *A = (HRplus + tmp1) * 0.5f; | |
135 *B = *A - tmp1; | |
136 | |
137 *(A + 1) = (HIminus + tmp2) * 0.5f; | |
138 *(B + 1) = (*(A + 1)) - HIminus; | |
139 | |
140 br1++; | |
141 br2--; | |
142 } | |
143 | |
144 // Handle DC bin separately | |
145 buffer[0] += buffer[1]; | |
146 buffer[1] = 0; | |
147 | |
148 return DMERR_OK; | |
149 } | |
750
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
150 |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
151 |
772
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
152 int dmConvertFFTtoFreqDomain(DMFFTContext *ctx, DMFFTType *buffer, |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
153 DMFFTType *real, DMFFTType *imag) |
750
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
154 { |
1691 | 155 if (ctx == NULL || buffer == NULL || |
156 real == NULL || imag == NULL) | |
750
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
157 return DMERR_NULLPTR; |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
158 |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
159 // Convert from bitreversed form to normal frequency domain |
772
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
160 const int *breversed = ctx->breversed; |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
161 |
1691 | 162 for (int i = 1; i < ctx->npoints; i++) |
750
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
163 { |
772
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
164 real[i] = buffer[breversed[i] ]; |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
165 imag[i] = buffer[breversed[i]+1]; |
750
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
166 } |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
167 |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
168 // Make the ends meet |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
169 real[0] = buffer[0]; |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
170 imag[0] = 0; |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
171 real[ctx->npoints] = buffer[1]; |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
172 imag[ctx->npoints] = 0; |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
173 |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
174 return DMERR_OK; |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
175 } |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
176 |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
177 |
772
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
178 int dmConvertFFTtoFreqAndPower(DMFFTContext *ctx, DMFFTType *buffer, |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
179 DMFFTType *real, DMFFTType *imag, DMFFTType *power, const DMFFTType scale) |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
180 { |
1691 | 181 if (ctx == NULL || buffer == NULL || |
182 real == NULL || imag == NULL || power == NULL) | |
772
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
183 return DMERR_NULLPTR; |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
184 |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
185 // Convert from bitreversed form to normal frequency domain |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
186 const int *breversed = ctx->breversed; |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
187 |
1691 | 188 for (int i = 1; i < ctx->npoints; i++) |
772
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
189 { |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
190 DMFFTType fre = real[i] = buffer[breversed[i] ], |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
191 fim = imag[i] = buffer[breversed[i]+1]; |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
192 |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
193 power[i] = DM_PW(fre, fim, scale); |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
194 } |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
195 |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
196 // Make the ends meet |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
197 real[0] = buffer[0]; |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
198 imag[0] = 0; |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
199 power[0] = DM_PW(buffer[0], 0, scale); |
1102
e06abfde6c39
Cosmetics pass: Remove excess whitespace.
Matti Hamalainen <ccr@tnsp.org>
parents:
812
diff
changeset
|
200 |
772
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
201 real[ctx->npoints] = buffer[1]; |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
202 imag[ctx->npoints] = 0; |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
203 power[ctx->npoints] = DM_PW(buffer[1], 0, scale); |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
204 |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
205 return DMERR_OK; |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
206 } |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
207 |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
208 |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
209 int dmConvertFFTtoPowerAndSum(DMFFTContext *ctx, DMFFTType *buffer, |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
210 DMFFTType *power, const DMFFTType pscale, DMFFTType *sum, const DMFFTType sscale) |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
211 { |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
212 if (ctx == NULL || buffer == NULL || power == NULL || sum == NULL) |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
213 return DMERR_NULLPTR; |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
214 |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
215 // Convert from bitreversed form to normal frequency domain |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
216 const int *breversed = ctx->breversed; |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
217 DMFFTType psum = 0; |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
218 |
1691 | 219 for (int i = 1; i < ctx->npoints; i++) |
772
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
220 { |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
221 DMFFTType fre = buffer[breversed[i] ], |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
222 fim = buffer[breversed[i]+1], |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
223 fpw = sqrt(fre * fre + fim * fim); |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
224 |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
225 power[i] = fpw * pscale; |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
226 psum += fpw * sscale; |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
227 } |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
228 |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
229 // Make the ends meet |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
230 power[0] = DM_PW(buffer[0], 0, pscale); |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
231 power[ctx->npoints] = DM_PW(buffer[1], 0, pscale); |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
232 |
773
95d9bd557a54
Return the power sum variable in the FFT conversion.
Matti Hamalainen <ccr@tnsp.org>
parents:
772
diff
changeset
|
233 *sum = psum; |
95d9bd557a54
Return the power sum variable in the FFT conversion.
Matti Hamalainen <ccr@tnsp.org>
parents:
772
diff
changeset
|
234 |
772
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
235 return DMERR_OK; |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
236 } |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
237 |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
238 |
750
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
239 int dmConvertFFTtoTimeDomain(DMFFTContext *ctx, DMFFTType *buffer, DMFFTType *tdom) |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
240 { |
772
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
241 int i, n; |
750
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
242 if (ctx == NULL || buffer == NULL || tdom == NULL) |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
243 return DMERR_NULLPTR; |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
244 |
772
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
245 for (n = i = 0; i < ctx->npoints; i++, n += 2) |
750
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
246 { |
772
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
247 tdom[n ] = buffer[ctx->breversed[i] ]; |
59df354b99cc
Add some utility functions in the FFT module.
Matti Hamalainen <ccr@tnsp.org>
parents:
751
diff
changeset
|
248 tdom[n+1] = buffer[ctx->breversed[i]+1]; |
750
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
249 } |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
250 |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
251 return DMERR_OK; |
e6d807ce715b
Add two more functions for converting FFT data to frequency and time
Matti Hamalainen <ccr@tnsp.org>
parents:
749
diff
changeset
|
252 } |