You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
469 lines
16 KiB
469 lines
16 KiB
15 years ago
|
#!/bin/sh
|
||
|
# GSL-GENFFT - Power2 FFT C Code Generator
|
||
|
# Copyright (C) 2001 Tim Janik
|
||
|
#
|
||
|
# This program is free software; you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU General Public License as published by
|
||
|
# the Free Software Foundation; either version 2 of the License, or
|
||
|
# (at your option) any later version.
|
||
|
#
|
||
|
# This program is distributed in the hope that it will be useful,
|
||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
# GNU General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU General Public License
|
||
|
# along with this program; if not, write to the Free Software
|
||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
||
|
|
||
|
# include files
|
||
|
echo "#include $2"
|
||
|
echo "#include <math.h>"
|
||
|
|
||
|
MKFFT="$1"
|
||
|
IEEE_TYPE="double"
|
||
|
OPTIONS="--double"
|
||
|
|
||
|
# provide macros and inline stubs
|
||
|
$MKFFT $OPTIONS 0
|
||
|
|
||
|
#
|
||
|
# generate small fft sizes, seperating stage 2
|
||
|
#
|
||
|
for dir in --analysis --synthesis ; do
|
||
|
DOPT="$OPTIONS --skip-macros $dir"
|
||
|
echo "Generating FFT functions: $dir" >&2
|
||
|
$MKFFT $DOPT 2 F # standalone fft2
|
||
|
$MKFFT $DOPT 4 S F # reusable fft4
|
||
|
$MKFFT $DOPT 4 F X # standalone fft4
|
||
|
$MKFFT $DOPT 8 s F F # reusable fft8 w/o input fft2
|
||
|
$MKFFT $DOPT 8 F s X # standalone fft8
|
||
|
$MKFFT $DOPT 16 s F F F # reusable fft16
|
||
|
$MKFFT $DOPT 16 F s s X # standalone fft16
|
||
|
$MKFFT $DOPT 32 s F F F F
|
||
|
$MKFFT $DOPT 32 F s s s X
|
||
|
$MKFFT $DOPT 64 s R R R R F # reusable fft64
|
||
|
$MKFFT $DOPT 64 F s s s s X # standalone fft64
|
||
|
$MKFFT $DOPT 128 s R R R R R F # reusable fft128
|
||
|
$MKFFT $DOPT 128 L s s s s s X #
|
||
|
$MKFFT $DOPT 256 s s s s s s X T # reuse fft128
|
||
|
$MKFFT $DOPT 256 L s s s s s s X #
|
||
|
$MKFFT $DOPT 512 s s s s s s X T T # reuse fft128
|
||
|
$MKFFT $DOPT 512 L s s s s s s s X # fft512
|
||
|
$MKFFT $DOPT 1024 s s s s s s s s X L #
|
||
|
$MKFFT $DOPT 1024 L s s s s s s s s X #
|
||
|
$MKFFT $DOPT 2048 s s s s s s s s X L L # reusable fft2048
|
||
|
$MKFFT $DOPT 2048 L s s s s s s s s s X #
|
||
|
$MKFFT $DOPT 4096 s s s s s s s s s s X L # reuses fft2048
|
||
|
$MKFFT $DOPT 4096 L s s s s s s s s s s X # fft4096
|
||
|
$MKFFT $DOPT 8192 s s s s s s s s s s s X L # reusable impl. for 8192, reuses 2048
|
||
|
$MKFFT $DOPT 8192 L s s s s s s s s s s s X # real impl. for 8192
|
||
|
done
|
||
|
|
||
|
#
|
||
|
# generic variable length implementation
|
||
|
#
|
||
|
echo "Generating generic FFT function for sizes >8192" >&2
|
||
|
cat <<__EOF
|
||
|
|
||
|
|
||
|
/* public FFT frontends and generic handling of huge fft sizes */
|
||
|
|
||
|
|
||
|
static void
|
||
|
gsl_power2_fftc_big (const unsigned int n_values,
|
||
|
const $IEEE_TYPE *rivalues_in,
|
||
|
$IEEE_TYPE *rivalues,
|
||
|
const int esign)
|
||
|
{
|
||
|
const unsigned int n_values2 = n_values << 1;
|
||
|
double theta = esign < 0 ? -3.1415926535897932384626433832795029 : 3.1415926535897932384626433832795029;
|
||
|
unsigned int i, block_size = 8192 << 1;
|
||
|
double last_sin;
|
||
|
|
||
|
if (esign > 0)
|
||
|
{
|
||
|
if (rivalues_in)
|
||
|
bitreverse_fft2analysis (n_values, rivalues_in, rivalues);
|
||
|
for (i = 0; i < n_values; i += 8192)
|
||
|
gsl_power2_fft8192analysis_skip2 (rivalues + (i << 1), rivalues + (i << 1));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (rivalues_in)
|
||
|
bitreverse_fft2synthesis (n_values, rivalues_in, rivalues);
|
||
|
for (i = 0; i < n_values; i += 8192)
|
||
|
gsl_power2_fft8192synthesis_skip2 (rivalues + (i << 1), rivalues + (i << 1));
|
||
|
}
|
||
|
theta *= (double) 1.0 / 8192.;
|
||
|
last_sin = sin (theta);
|
||
|
|
||
|
/* we're not combining the first and second halves coefficients
|
||
|
* in the below loop, since for fft sizes beyond 8192, it'd actually
|
||
|
* harm performance due to paging
|
||
|
*/
|
||
|
do
|
||
|
{
|
||
|
double Dre, Dim, Wre, Wim;
|
||
|
unsigned int k, i, half_block = block_size >> 1;
|
||
|
unsigned int block_size2 = block_size << 1;
|
||
|
|
||
|
theta *= 0.5;
|
||
|
Dim = last_sin;
|
||
|
last_sin = sin (theta);
|
||
|
Dre = last_sin * last_sin * -2.;
|
||
|
|
||
|
/* loop over first coefficient in each block ==> w == {1,0} */
|
||
|
for (i = 0; i < n_values2; i += block_size2)
|
||
|
{
|
||
|
unsigned int v1 = i, v2 = i + block_size;
|
||
|
|
||
|
BUTTERFLY_10 (rivalues[v1], rivalues[v1 + 1],
|
||
|
rivalues[v2], rivalues[v2 + 1],
|
||
|
rivalues[v1], rivalues[v1 + 1],
|
||
|
rivalues[v2], rivalues[v2 + 1],
|
||
|
__1, __0);
|
||
|
}
|
||
|
Wre = Dre + 1.0; /* update Wk */
|
||
|
Wim = Dim; /* update Wk */
|
||
|
/* loop for every Wk in the first half of each subblock */
|
||
|
for (k = 2; k < half_block; k += 2)
|
||
|
{
|
||
|
/* loop over kth coefficient in each block */
|
||
|
for (i = k; i < n_values2; i += block_size2)
|
||
|
{
|
||
|
unsigned int v1 = i, v2 = i + block_size;
|
||
|
|
||
|
BUTTERFLY_XY (rivalues[v1], rivalues[v1 + 1],
|
||
|
rivalues[v2], rivalues[v2 + 1],
|
||
|
rivalues[v1], rivalues[v1 + 1],
|
||
|
rivalues[v2], rivalues[v2 + 1],
|
||
|
Wre, Wim);
|
||
|
}
|
||
|
WMULTIPLY (Wre, Wim, Dre, Dim); /* update Wk */
|
||
|
}
|
||
|
/* handle middle coefficient ==> w == {0,+-1} */
|
||
|
if (k < block_size)
|
||
|
{
|
||
|
/* loop over kth coefficient in each block */
|
||
|
if (esign > 0)
|
||
|
for (i = k; i < n_values2; i += block_size2)
|
||
|
{
|
||
|
unsigned int v1 = i, v2 = i + block_size;
|
||
|
|
||
|
BUTTERFLY_01 (rivalues[v1], rivalues[v1 + 1],
|
||
|
rivalues[v2], rivalues[v2 + 1],
|
||
|
rivalues[v1], rivalues[v1 + 1],
|
||
|
rivalues[v2], rivalues[v2 + 1],
|
||
|
__0, __1);
|
||
|
}
|
||
|
else
|
||
|
for (i = k; i < n_values2; i += block_size2)
|
||
|
{
|
||
|
unsigned int v1 = i, v2 = i + block_size;
|
||
|
|
||
|
BUTTERFLY_0m (rivalues[v1], rivalues[v1 + 1],
|
||
|
rivalues[v2], rivalues[v2 + 1],
|
||
|
rivalues[v1], rivalues[v1 + 1],
|
||
|
rivalues[v2], rivalues[v2 + 1],
|
||
|
__0, __1);
|
||
|
}
|
||
|
/* update Wk */
|
||
|
if (esign > 0)
|
||
|
{
|
||
|
Wre = -Dim;
|
||
|
Wim = Dre + 1.0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Wre = Dim;
|
||
|
Wim = -Dre - 1.0;
|
||
|
}
|
||
|
k += 2;
|
||
|
}
|
||
|
/* loop for every Wk in the second half of each subblock */
|
||
|
for (; k < block_size; k += 2)
|
||
|
{
|
||
|
/* loop over kth coefficient in each block */
|
||
|
for (i = k; i < n_values2; i += block_size2)
|
||
|
{
|
||
|
unsigned int v1 = i, v2 = i + block_size;
|
||
|
|
||
|
BUTTERFLY_XY (rivalues[v1], rivalues[v1 + 1],
|
||
|
rivalues[v2], rivalues[v2 + 1],
|
||
|
rivalues[v1], rivalues[v1 + 1],
|
||
|
rivalues[v2], rivalues[v2 + 1],
|
||
|
Wre, Wim);
|
||
|
}
|
||
|
WMULTIPLY (Wre, Wim, Dre, Dim); /* update Wk */
|
||
|
}
|
||
|
block_size = block_size2;
|
||
|
}
|
||
|
while (block_size <= n_values);
|
||
|
}
|
||
|
__EOF
|
||
|
|
||
|
|
||
|
#
|
||
|
# public complex fft frontends
|
||
|
#
|
||
|
echo "Generating public complex FFT frontends" >&2
|
||
|
cat <<__EOF
|
||
|
void
|
||
|
gsl_power2_fftac (const unsigned int n_values,
|
||
|
const $IEEE_TYPE *rivalues_in,
|
||
|
$IEEE_TYPE *rivalues_out)
|
||
|
{
|
||
|
g_return_if_fail ((n_values & (n_values - 1)) == 0 && n_values >= 1);
|
||
|
|
||
|
switch (n_values)
|
||
|
{
|
||
|
case 1: rivalues_out[0] = rivalues_in[0], rivalues_out[1] = rivalues_in[1]; break;
|
||
|
case 2: gsl_power2_fft2analysis (rivalues_in, rivalues_out); break;
|
||
|
case 4: gsl_power2_fft4analysis (rivalues_in, rivalues_out); break;
|
||
|
case 8: gsl_power2_fft8analysis (rivalues_in, rivalues_out); break;
|
||
|
case 16: gsl_power2_fft16analysis (rivalues_in, rivalues_out); break;
|
||
|
case 32: gsl_power2_fft32analysis (rivalues_in, rivalues_out); break;
|
||
|
case 64: gsl_power2_fft64analysis (rivalues_in, rivalues_out); break;
|
||
|
case 128: gsl_power2_fft128analysis (rivalues_in, rivalues_out); break;
|
||
|
case 256: gsl_power2_fft256analysis (rivalues_in, rivalues_out); break;
|
||
|
case 512: gsl_power2_fft512analysis (rivalues_in, rivalues_out); break;
|
||
|
case 1024: gsl_power2_fft1024analysis (rivalues_in, rivalues_out); break;
|
||
|
case 2048: gsl_power2_fft2048analysis (rivalues_in, rivalues_out); break;
|
||
|
case 4096: gsl_power2_fft4096analysis (rivalues_in, rivalues_out); break;
|
||
|
case 8192: gsl_power2_fft8192analysis (rivalues_in, rivalues_out); break;
|
||
|
default: gsl_power2_fftc_big (n_values, rivalues_in, rivalues_out, +1);
|
||
|
}
|
||
|
}
|
||
|
void
|
||
|
gsl_power2_fftsc (const unsigned int n_values,
|
||
|
const $IEEE_TYPE *rivalues_in,
|
||
|
$IEEE_TYPE *rivalues_out)
|
||
|
{
|
||
|
g_return_if_fail ((n_values & (n_values - 1)) == 0 && n_values >= 1);
|
||
|
|
||
|
switch (n_values)
|
||
|
{
|
||
|
case 1: rivalues_out[0] = rivalues_in[0], rivalues_out[1] = rivalues_in[1]; break;
|
||
|
case 2: gsl_power2_fft2synthesis (rivalues_in, rivalues_out); break;
|
||
|
case 4: gsl_power2_fft4synthesis (rivalues_in, rivalues_out); break;
|
||
|
case 8: gsl_power2_fft8synthesis (rivalues_in, rivalues_out); break;
|
||
|
case 16: gsl_power2_fft16synthesis (rivalues_in, rivalues_out); break;
|
||
|
case 32: gsl_power2_fft32synthesis (rivalues_in, rivalues_out); break;
|
||
|
case 64: gsl_power2_fft64synthesis (rivalues_in, rivalues_out); break;
|
||
|
case 128: gsl_power2_fft128synthesis (rivalues_in, rivalues_out); break;
|
||
|
case 256: gsl_power2_fft256synthesis (rivalues_in, rivalues_out); break;
|
||
|
case 512: gsl_power2_fft512synthesis (rivalues_in, rivalues_out); break;
|
||
|
case 1024: gsl_power2_fft1024synthesis (rivalues_in, rivalues_out); break;
|
||
|
case 2048: gsl_power2_fft2048synthesis (rivalues_in, rivalues_out); break;
|
||
|
case 4096: gsl_power2_fft4096synthesis (rivalues_in, rivalues_out); break;
|
||
|
case 8192: gsl_power2_fft8192synthesis (rivalues_in, rivalues_out); break;
|
||
|
default: gsl_power2_fftc_big (n_values, rivalues_in, rivalues_out, -1);
|
||
|
}
|
||
|
/* { unsigned int i; for (i = 0; i < n_values << 1; i++) rivalues_out[i] *= (double) n_values; } */
|
||
|
}
|
||
|
__EOF
|
||
|
|
||
|
|
||
|
#
|
||
|
# public real fft frontends
|
||
|
#
|
||
|
echo "Generating public real FFT frontends" >&2
|
||
|
cat <<__EOF
|
||
|
void
|
||
|
gsl_power2_fftar (const unsigned int n_values,
|
||
|
const $IEEE_TYPE *r_values_in,
|
||
|
$IEEE_TYPE *rivalues_out)
|
||
|
{
|
||
|
unsigned int n_cvalues = n_values >> 1;
|
||
|
double Dre, Dim, Wre, Wim, theta;
|
||
|
unsigned int i;
|
||
|
|
||
|
g_return_if_fail ((n_values & (n_values - 1)) == 0 && n_values >= 2);
|
||
|
|
||
|
gsl_power2_fftac (n_cvalues, r_values_in, rivalues_out);
|
||
|
theta = 3.1415926535897932384626433832795029;
|
||
|
theta /= (double) n_cvalues;
|
||
|
|
||
|
Dre = sin (0.5 * theta);
|
||
|
Dim = sin (theta);
|
||
|
Dre = Dre * Dre;
|
||
|
Wre = 0.5 - Dre;
|
||
|
Dre *= -2.;
|
||
|
Wim = Dim * 0.5;
|
||
|
for (i = 2; i < n_values >> 1; i += 2)
|
||
|
{
|
||
|
double F1re, F1im, F2re, F2im, H1re, H1im, H2re, H2im;
|
||
|
unsigned int r = n_values - i;
|
||
|
double FEre = rivalues_out[i] + rivalues_out[r];
|
||
|
double FEim = rivalues_out[i + 1] - rivalues_out[r + 1];
|
||
|
double FOre = rivalues_out[r] - rivalues_out[i];
|
||
|
double FOim = rivalues_out[r + 1] + rivalues_out[i + 1];
|
||
|
|
||
|
FEre *= 0.5;
|
||
|
FEim *= 0.5;
|
||
|
F2re = FOre * Wim;
|
||
|
F2im = FOim * Wim;
|
||
|
F1re = FOre * Wre;
|
||
|
F1im = FOim * Wre;
|
||
|
|
||
|
H1im = F2im + F1re;
|
||
|
H1re = F1im - F2re;
|
||
|
H2re = F2re - F1im;
|
||
|
H2im = H1im - FEim;
|
||
|
H1re += FEre;
|
||
|
H2re += FEre;
|
||
|
H1im += FEim;
|
||
|
rivalues_out[i] = H1re;
|
||
|
rivalues_out[i + 1] = H1im;
|
||
|
rivalues_out[r] = H2re;
|
||
|
rivalues_out[r + 1] = H2im;
|
||
|
WMULTIPLY (Wre, Wim, Dre, Dim);
|
||
|
}
|
||
|
Dre = rivalues_out[0];
|
||
|
rivalues_out[0] = Dre + rivalues_out[1];
|
||
|
rivalues_out[1] = Dre - rivalues_out[1];
|
||
|
}
|
||
|
void
|
||
|
gsl_power2_fftsr (const unsigned int n_values,
|
||
|
const double *rivalues_in,
|
||
|
double *r_values_out)
|
||
|
{
|
||
|
unsigned int n_cvalues = n_values >> 1;
|
||
|
double Dre, Dim, Wre, Wim, theta, scale;
|
||
|
unsigned int i, ri;
|
||
|
|
||
|
g_return_if_fail ((n_values & (n_values - 1)) == 0 && n_values >= 2);
|
||
|
|
||
|
theta = -3.1415926535897932384626433832795029;
|
||
|
theta /= (double) n_cvalues;
|
||
|
|
||
|
Dre = sin (0.5 * theta);
|
||
|
Dim = sin (theta);
|
||
|
Dre = Dre * Dre;
|
||
|
Wre = 0.5 - Dre;
|
||
|
Dre *= -2.;
|
||
|
Wim = Dim * 0.5;
|
||
|
for (i = 2, ri = 0; i < n_values >> 1; i += 2)
|
||
|
{
|
||
|
double F1re, F1im, F2re, F2im, H1re, H1im, H2re, H2im;
|
||
|
unsigned int g = n_values - i, j = n_values >> 2, rg = n_values - (ri << 1) - 2;
|
||
|
double FEre = rivalues_in[i] + rivalues_in[g];
|
||
|
double FEim = rivalues_in[i + 1] - rivalues_in[g + 1];
|
||
|
double FOre = rivalues_in[g] - rivalues_in[i];
|
||
|
double FOim = rivalues_in[g + 1] + rivalues_in[i + 1];
|
||
|
|
||
|
while (ri >= j)
|
||
|
{
|
||
|
ri -= j;
|
||
|
j >>= 1;
|
||
|
}
|
||
|
ri |= j;
|
||
|
|
||
|
FOre = -FOre;
|
||
|
FOim = -FOim;
|
||
|
FEre *= 0.5;
|
||
|
FEim *= 0.5;
|
||
|
F2re = FOre * Wim;
|
||
|
F2im = FOim * Wim;
|
||
|
F1re = FOre * Wre;
|
||
|
F1im = FOim * Wre;
|
||
|
|
||
|
H1im = F2im + F1re;
|
||
|
H1re = F1im - F2re;
|
||
|
H2re = F2re - F1im;
|
||
|
H2im = H1im - FEim;
|
||
|
H1re += FEre;
|
||
|
H2re += FEre;
|
||
|
H1im += FEim;
|
||
|
|
||
|
j = ri << 1;
|
||
|
r_values_out[j] = H1re;
|
||
|
r_values_out[j + 1] = H1im;
|
||
|
r_values_out[rg] = H2re;
|
||
|
r_values_out[rg + 1] = H2im;
|
||
|
WMULTIPLY (Wre, Wim, Dre, Dim);
|
||
|
}
|
||
|
Dre = rivalues_in[0];
|
||
|
r_values_out[0] = Dre + rivalues_in[1];
|
||
|
r_values_out[1] = Dre - rivalues_in[1];
|
||
|
r_values_out[0] *= 0.5;
|
||
|
r_values_out[1] *= 0.5;
|
||
|
if (n_values < 4)
|
||
|
return;
|
||
|
r_values_out[2] = rivalues_in[i];
|
||
|
r_values_out[2 + 1] = rivalues_in[i + 1];
|
||
|
scale = n_cvalues;
|
||
|
scale = 1.0 / scale;
|
||
|
for (i = 0; i < n_values; i += 4)
|
||
|
BUTTERFLY_10scale (r_values_out[i], r_values_out[i + 1],
|
||
|
r_values_out[i + 2], r_values_out[i + 3],
|
||
|
r_values_out[i], r_values_out[i + 1],
|
||
|
r_values_out[i + 2], r_values_out[i + 3],
|
||
|
scale);
|
||
|
switch (n_cvalues)
|
||
|
{
|
||
|
case 2: break;
|
||
|
case 4: gsl_power2_fft4synthesis_skip2 (NULL, r_values_out); break;
|
||
|
case 8: gsl_power2_fft8synthesis_skip2 (NULL, r_values_out); break;
|
||
|
case 16: gsl_power2_fft16synthesis_skip2 (NULL, r_values_out); break;
|
||
|
case 32: gsl_power2_fft32synthesis_skip2 (NULL, r_values_out); break;
|
||
|
case 64: gsl_power2_fft64synthesis_skip2 (NULL, r_values_out); break;
|
||
|
case 128: gsl_power2_fft128synthesis_skip2 (NULL, r_values_out); break;
|
||
|
case 256: gsl_power2_fft256synthesis_skip2 (NULL, r_values_out); break;
|
||
|
case 512: gsl_power2_fft512synthesis_skip2 (NULL, r_values_out); break;
|
||
|
case 1024: gsl_power2_fft1024synthesis_skip2 (NULL, r_values_out); break;
|
||
|
case 2048: gsl_power2_fft2048synthesis_skip2 (NULL, r_values_out); break;
|
||
|
case 4096: gsl_power2_fft4096synthesis_skip2 (NULL, r_values_out); break;
|
||
|
case 8192: gsl_power2_fft8192synthesis_skip2 (NULL, r_values_out); break;
|
||
|
default: gsl_power2_fftc_big (n_cvalues, NULL, r_values_out, -1);
|
||
|
}
|
||
|
}
|
||
|
void
|
||
|
gsl_power2_fftar_simple (const unsigned int n_values,
|
||
|
const float *real_values,
|
||
|
float *complex_values)
|
||
|
{
|
||
|
double *rv, *cv;
|
||
|
guint i;
|
||
|
|
||
|
g_return_if_fail ((n_values & (n_values - 1)) == 0 && n_values >= 2);
|
||
|
|
||
|
rv = g_new (double, n_values * 2);
|
||
|
cv = rv + n_values;
|
||
|
i = n_values;
|
||
|
while (i--)
|
||
|
rv[i] = real_values[i];
|
||
|
gsl_power2_fftar (n_values, rv, cv);
|
||
|
i = n_values;
|
||
|
while (i--)
|
||
|
complex_values[i] = cv[i];
|
||
|
complex_values[n_values] = complex_values[1];
|
||
|
complex_values[1] = 0.0;
|
||
|
complex_values[n_values + 1] = 0.0;
|
||
|
g_free (rv);
|
||
|
}
|
||
|
void
|
||
|
gsl_power2_fftsr_simple (const unsigned int n_values,
|
||
|
const float *complex_values,
|
||
|
float *real_values)
|
||
|
{
|
||
|
double *cv, *rv;
|
||
|
guint i;
|
||
|
|
||
|
g_return_if_fail ((n_values & (n_values - 1)) == 0 && n_values >= 2);
|
||
|
|
||
|
cv = g_new (double, n_values * 2);
|
||
|
rv = cv + n_values;
|
||
|
i = n_values;
|
||
|
while (i--)
|
||
|
cv[i] = complex_values[i];
|
||
|
cv[1] = complex_values[n_values];
|
||
|
gsl_power2_fftsr (n_values, cv, rv);
|
||
|
i = n_values;
|
||
|
while (i--)
|
||
|
real_values[i] = rv[i];
|
||
|
g_free (cv);
|
||
|
}
|
||
|
__EOF
|