crmf 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +12 -0
- data/crmf.gemspec +105 -3
- data/ext/crlibm-1.0beta5/AUTHORS +2 -0
- data/ext/crlibm-1.0beta5/CMakeLists.txt +154 -0
- data/ext/crlibm-1.0beta5/COPYING +340 -0
- data/ext/crlibm-1.0beta5/COPYING.LIB +504 -0
- data/ext/crlibm-1.0beta5/ChangeLog +125 -0
- data/ext/crlibm-1.0beta5/Makefile.am +134 -0
- data/ext/crlibm-1.0beta5/NEWS +0 -0
- data/ext/crlibm-1.0beta5/README +31 -0
- data/ext/crlibm-1.0beta5/README.DEV +23 -0
- data/ext/crlibm-1.0beta5/README.md +5 -0
- data/ext/crlibm-1.0beta5/TODO +66 -0
- data/ext/crlibm-1.0beta5/VERSION +1 -0
- data/ext/crlibm-1.0beta5/acos-td.c +1195 -0
- data/ext/crlibm-1.0beta5/acos-td.h +629 -0
- data/ext/crlibm-1.0beta5/asin-td.c +1297 -0
- data/ext/crlibm-1.0beta5/asin-td.h +620 -0
- data/ext/crlibm-1.0beta5/asincos.c +4488 -0
- data/ext/crlibm-1.0beta5/asincos.h +575 -0
- data/ext/crlibm-1.0beta5/atan-itanium.c +846 -0
- data/ext/crlibm-1.0beta5/atan-pentium.c +280 -0
- data/ext/crlibm-1.0beta5/atan-pentium.h +343 -0
- data/ext/crlibm-1.0beta5/atan_accurate.c +341 -0
- data/ext/crlibm-1.0beta5/atan_accurate.h +198 -0
- data/ext/crlibm-1.0beta5/atan_fast.c +506 -0
- data/ext/crlibm-1.0beta5/atan_fast.h +680 -0
- data/ext/crlibm-1.0beta5/configure.ac +419 -0
- data/ext/crlibm-1.0beta5/crlibm.h +204 -0
- data/ext/crlibm-1.0beta5/crlibm.spec +42 -0
- data/ext/crlibm-1.0beta5/crlibm_private.c +397 -0
- data/ext/crlibm-1.0beta5/crlibm_private.h +1048 -0
- data/ext/crlibm-1.0beta5/csh_fast.c +721 -0
- data/ext/crlibm-1.0beta5/csh_fast.h +771 -0
- data/ext/crlibm-1.0beta5/double-extended.h +496 -0
- data/ext/crlibm-1.0beta5/exp-itanium.c +723 -0
- data/ext/crlibm-1.0beta5/exp-td-standalone.c +87 -0
- data/ext/crlibm-1.0beta5/exp-td.c +1363 -0
- data/ext/crlibm-1.0beta5/exp-td.h +685 -0
- data/ext/crlibm-1.0beta5/exp_build_coeffs/exp_fast_table.c +125 -0
- data/ext/crlibm-1.0beta5/expm1-standalone.c +119 -0
- data/ext/crlibm-1.0beta5/expm1.c +2515 -0
- data/ext/crlibm-1.0beta5/expm1.h +715 -0
- data/ext/crlibm-1.0beta5/interval.h +238 -0
- data/ext/crlibm-1.0beta5/log-de.c +480 -0
- data/ext/crlibm-1.0beta5/log-de.h +747 -0
- data/ext/crlibm-1.0beta5/log-de2.c +280 -0
- data/ext/crlibm-1.0beta5/log-de2.h +2352 -0
- data/ext/crlibm-1.0beta5/log-td.c +1158 -0
- data/ext/crlibm-1.0beta5/log-td.h +819 -0
- data/ext/crlibm-1.0beta5/log.c +2244 -0
- data/ext/crlibm-1.0beta5/log.h +1592 -0
- data/ext/crlibm-1.0beta5/log10-td.c +906 -0
- data/ext/crlibm-1.0beta5/log10-td.h +823 -0
- data/ext/crlibm-1.0beta5/log1p.c +1295 -0
- data/ext/crlibm-1.0beta5/log2-td.c +1521 -0
- data/ext/crlibm-1.0beta5/log2-td.h +821 -0
- data/ext/crlibm-1.0beta5/log2_accurate.c +330 -0
- data/ext/crlibm-1.0beta5/log2_accurate.h +261 -0
- data/ext/crlibm-1.0beta5/log_accurate.c +133 -0
- data/ext/crlibm-1.0beta5/log_accurate.h +261 -0
- data/ext/crlibm-1.0beta5/log_fast.c +360 -0
- data/ext/crlibm-1.0beta5/log_fast.h +440 -0
- data/ext/crlibm-1.0beta5/pow.c +1396 -0
- data/ext/crlibm-1.0beta5/pow.h +3101 -0
- data/ext/crlibm-1.0beta5/prepare +20 -0
- data/ext/crlibm-1.0beta5/rem_pio2_accurate.c +219 -0
- data/ext/crlibm-1.0beta5/rem_pio2_accurate.h +53 -0
- data/ext/crlibm-1.0beta5/scs_lib/AUTHORS +3 -0
- data/ext/crlibm-1.0beta5/scs_lib/COPYING +504 -0
- data/ext/crlibm-1.0beta5/scs_lib/ChangeLog +16 -0
- data/ext/crlibm-1.0beta5/scs_lib/Doxyfile.dev +939 -0
- data/ext/crlibm-1.0beta5/scs_lib/Doxyfile.user +939 -0
- data/ext/crlibm-1.0beta5/scs_lib/INSTALL +215 -0
- data/ext/crlibm-1.0beta5/scs_lib/Makefile.am +17 -0
- data/ext/crlibm-1.0beta5/scs_lib/NEWS +0 -0
- data/ext/crlibm-1.0beta5/scs_lib/README +9 -0
- data/ext/crlibm-1.0beta5/scs_lib/README.DEV +38 -0
- data/ext/crlibm-1.0beta5/scs_lib/TODO +4 -0
- data/ext/crlibm-1.0beta5/scs_lib/VERSION +1 -0
- data/ext/crlibm-1.0beta5/scs_lib/addition_scs.c +623 -0
- data/ext/crlibm-1.0beta5/scs_lib/division_scs.c +110 -0
- data/ext/crlibm-1.0beta5/scs_lib/double2scs.c +174 -0
- data/ext/crlibm-1.0beta5/scs_lib/main.dox +104 -0
- data/ext/crlibm-1.0beta5/scs_lib/multiplication_scs.c +339 -0
- data/ext/crlibm-1.0beta5/scs_lib/poly_fct.c +112 -0
- data/ext/crlibm-1.0beta5/scs_lib/print_scs.c +73 -0
- data/ext/crlibm-1.0beta5/scs_lib/rand_scs.c +63 -0
- data/ext/crlibm-1.0beta5/scs_lib/scs.h +353 -0
- data/ext/crlibm-1.0beta5/scs_lib/scs2double.c +411 -0
- data/ext/crlibm-1.0beta5/scs_lib/scs2mpf.c +58 -0
- data/ext/crlibm-1.0beta5/scs_lib/scs2mpfr.c +61 -0
- data/ext/crlibm-1.0beta5/scs_lib/scs_private.c +23 -0
- data/ext/crlibm-1.0beta5/scs_lib/scs_private.h +133 -0
- data/ext/crlibm-1.0beta5/scs_lib/wrapper_scs.h +486 -0
- data/ext/crlibm-1.0beta5/scs_lib/zero_scs.c +52 -0
- data/ext/crlibm-1.0beta5/trigo_accurate.c +501 -0
- data/ext/crlibm-1.0beta5/trigo_accurate.h +331 -0
- data/ext/crlibm-1.0beta5/trigo_fast.c +1243 -0
- data/ext/crlibm-1.0beta5/trigo_fast.h +639 -0
- data/ext/crlibm-1.0beta5/trigpi.c +1169 -0
- data/ext/crlibm-1.0beta5/trigpi.h +556 -0
- data/ext/crlibm-1.0beta5/triple-double.c +57 -0
- data/ext/crlibm-1.0beta5/triple-double.h +1380 -0
- data/ext/crmf/crmf.c +117 -20
- data/ext/crmf/extconf.rb +12 -8
- data/lib/crmf/version.rb +1 -1
- data/tests/perf.rb +100 -219
- metadata +108 -10
- data/ext/crlibm-1.0beta4.tar.gz +0 -0
@@ -0,0 +1,353 @@
|
|
1
|
+
/** This is the main header file of the SCS library, which defines the
|
2
|
+
SCS data structure, and the functions that implement arithmetic on it.
|
3
|
+
|
4
|
+
@file scs.h
|
5
|
+
|
6
|
+
@author David Defour David.Defour@ens-lyon.fr
|
7
|
+
@author Florent de Dinechin Florent.de.Dinechin@ens-lyon.fr
|
8
|
+
|
9
|
+
This file is part of the SCS library.
|
10
|
+
|
11
|
+
Copyright (C) 2002 David Defour and Florent de Dinechin
|
12
|
+
|
13
|
+
This library is free software; you can redistribute it and/or
|
14
|
+
modify it under the terms of the GNU Lesser General Public
|
15
|
+
License as published by the Free Software Foundation; either
|
16
|
+
version 2.1 of the License, or (at your option) any later version.
|
17
|
+
|
18
|
+
This library is distributed in the hope that it will be useful,
|
19
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
20
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
21
|
+
Lesser General Public License for more details.
|
22
|
+
|
23
|
+
You should have received a copy of the GNU Lesser General Public
|
24
|
+
License along with this library; if not, write to the Free Software
|
25
|
+
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
26
|
+
|
27
|
+
*/
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
/* Avoid loading the header twice */
|
32
|
+
#ifndef INCLUDE_SCS
|
33
|
+
#define INCLUDE_SCS 1
|
34
|
+
|
35
|
+
#ifndef DOXYGEN_SHOULD_SKIP_THIS /* because it is not very clean */
|
36
|
+
|
37
|
+
#ifdef HAVE_CONFIG_H
|
38
|
+
#include "../crlibm_config.h"
|
39
|
+
#endif
|
40
|
+
#ifdef HAVE_INTTYPES_H
|
41
|
+
#include <inttypes.h>
|
42
|
+
#endif
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
/* 64 bit arithmetic may be standardised, but people still do want they want */
|
47
|
+
#ifdef HAVE_INTTYPES_H
|
48
|
+
#define ULL(bits) 0x##bits##uLL
|
49
|
+
#elif defined(WIN32)
|
50
|
+
/* TODO insert Windows garbage there */
|
51
|
+
/* Default, hoping it works, hopefully less and less relevant */
|
52
|
+
#else
|
53
|
+
typedef long long int64_t;
|
54
|
+
typedef unsigned long long uint64_t;
|
55
|
+
#define ULL(bits) 0x##bits##uLL
|
56
|
+
#endif
|
57
|
+
|
58
|
+
#ifndef SCS_DEF_INT64
|
59
|
+
#define SCS_DEF_INT64
|
60
|
+
#ifdef SCS_TYPEOS_HPUX
|
61
|
+
#ifndef __LP64__ /* To solve the problem with 64 bits integer */
|
62
|
+
typedef long long int64_t;
|
63
|
+
typedef unsigned long long uint64_t;
|
64
|
+
#define ULL(bits) 0x##bits##uLL
|
65
|
+
#endif
|
66
|
+
#endif
|
67
|
+
#endif
|
68
|
+
|
69
|
+
|
70
|
+
#ifdef HAVE_GMP_H
|
71
|
+
#include <gmp.h>
|
72
|
+
#endif
|
73
|
+
|
74
|
+
#ifdef HAVE_MPFR_H
|
75
|
+
#include <mpfr.h>
|
76
|
+
#endif
|
77
|
+
|
78
|
+
|
79
|
+
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
|
80
|
+
|
81
|
+
|
82
|
+
/** @internal An union to cast floats into doubles or the other way round. For
|
83
|
+
internal purpose only */
|
84
|
+
|
85
|
+
typedef union {
|
86
|
+
int32_t i[2]; /* Signed (may be useful) */
|
87
|
+
int64_t l; /* Signed (may be useful) */
|
88
|
+
double d;
|
89
|
+
} db_number;
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
/* ****************************************************************** */
|
96
|
+
/**@name SCS data-types */ /**@{*/
|
97
|
+
|
98
|
+
/** @struct scs
|
99
|
+
The SCS data type.
|
100
|
+
|
101
|
+
An SCS number is a a floating-point number in base 2^32.
|
102
|
+
|
103
|
+
- Its mantissa is formed of SCS_NB_WORDS digits (currently 32 bits by default)
|
104
|
+
|
105
|
+
- Its exponent is a 32-bit integer
|
106
|
+
|
107
|
+
- It also has a sign field, and an exception field used to store and
|
108
|
+
propagate IEEE-754 exceptions.
|
109
|
+
|
110
|
+
|
111
|
+
The real number represented by a scs structure is equal to:
|
112
|
+
@f$
|
113
|
+
\displaystyle
|
114
|
+
\sum_{i=0}^{\mathtt{SCS\_NB\_WORDS}}
|
115
|
+
2^{(\mathtt{index} -i)\mathtt{SCS\_NB\_BITS}}
|
116
|
+
\times
|
117
|
+
\mathtt{h\_word}[i]
|
118
|
+
@f$
|
119
|
+
*/
|
120
|
+
|
121
|
+
/*
|
122
|
+
(verbatim-mode formula for the above eqation:) the number represented by a
|
123
|
+
SCS structure is :
|
124
|
+
|
125
|
+
i<SCS_NB_WORDS (index - i).SCS_NB_BITS
|
126
|
+
sign . ( sum ( h_word[i] . 2^ )
|
127
|
+
i=0
|
128
|
+
*/
|
129
|
+
|
130
|
+
struct scs {
|
131
|
+
/** the digits, as 32 bits words */
|
132
|
+
uint32_t h_word[SCS_NB_WORDS];
|
133
|
+
/** Used to store Nan,+/-0, Inf, etc and then let the hardware handle them */
|
134
|
+
db_number exception;
|
135
|
+
/** This corresponds to the exponent in an FP format, but here we are
|
136
|
+
in base 2^32 */
|
137
|
+
int index;
|
138
|
+
/** The sign equals 1 or -1*/
|
139
|
+
int sign;
|
140
|
+
};
|
141
|
+
|
142
|
+
|
143
|
+
typedef struct scs scs;
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
/** scs_ptr is a pointer on a SCS structure */
|
148
|
+
typedef struct scs * scs_ptr;
|
149
|
+
|
150
|
+
|
151
|
+
|
152
|
+
|
153
|
+
/** scs_t is an array of one SCS struct to lighten syntax : you may
|
154
|
+
declare a scs_t object, and pass it to the scs functions (which
|
155
|
+
expect pointers) without using ampersands.
|
156
|
+
*/
|
157
|
+
typedef struct scs scs_t[1];
|
158
|
+
|
159
|
+
/**@}*/ /* end doxygen group for SCS data-types */
|
160
|
+
|
161
|
+
|
162
|
+
|
163
|
+
|
164
|
+
|
165
|
+
|
166
|
+
|
167
|
+
|
168
|
+
/* ****************************************************************** */
|
169
|
+
/**@name Conversion and initialization functions */ /**@{*/
|
170
|
+
|
171
|
+
/** Convert a SCS number to a double, rounding to the nearest */
|
172
|
+
void scs_get_d(double*, scs_ptr);
|
173
|
+
|
174
|
+
/** Convert a SCS number to a double, rounding towards minus infinity */
|
175
|
+
void scs_get_d_minf(double*, scs_ptr);
|
176
|
+
|
177
|
+
/** Convert a SCS number to a double, rounding towards plus infinity */
|
178
|
+
void scs_get_d_pinf(double*, scs_ptr);
|
179
|
+
|
180
|
+
/** Convert a SCS number to a double, rounding towards zero */
|
181
|
+
void scs_get_d_zero(double*, scs_ptr);
|
182
|
+
|
183
|
+
/** Convert a double into a SCS number (this is an exact operation) */
|
184
|
+
void scs_set_d(scs_ptr, double);
|
185
|
+
|
186
|
+
/** Convert a signed int into a SCS number (this is an exact operation) */
|
187
|
+
void scs_set_si(scs_ptr, signed int);
|
188
|
+
|
189
|
+
|
190
|
+
/** Print out a SCS number. Sorry for the strange name, we are mimicking GMP */
|
191
|
+
void scs_get_std(scs_ptr);
|
192
|
+
|
193
|
+
|
194
|
+
/** Copy a SCS number into another */
|
195
|
+
void scs_set(scs_ptr, scs_ptr);
|
196
|
+
|
197
|
+
|
198
|
+
/** Set a SCS number to zero */
|
199
|
+
void scs_zero(scs_ptr);
|
200
|
+
|
201
|
+
|
202
|
+
/** Generate a random SCS number.
|
203
|
+
The index field of result will be between -expo_max and +expo_max.
|
204
|
+
Example: to get a number in the double-precision floating-point range,
|
205
|
+
expo_max should be smaller than 39.
|
206
|
+
@warning No guarantee is made about the quality of the random algorithm
|
207
|
+
used... */
|
208
|
+
void scs_rand(scs_ptr result, int expo_max);
|
209
|
+
|
210
|
+
/**@}*/ /* end doxygen group for conversion / initialisation functions*/
|
211
|
+
|
212
|
+
|
213
|
+
|
214
|
+
|
215
|
+
/* ****************************************************************** */
|
216
|
+
/**@name Addition and renormalisation functions */ /**@{*/
|
217
|
+
|
218
|
+
|
219
|
+
/** Addition of two SCS numbers.
|
220
|
+
The arguments x, y and result may point to the same memory
|
221
|
+
location. The result is a normalised SCS number.
|
222
|
+
*/
|
223
|
+
void scs_add(scs_ptr result, scs_ptr x, scs_ptr y);
|
224
|
+
|
225
|
+
|
226
|
+
/** Subtraction of two SCS numbers.
|
227
|
+
The arguments x, y and result may point to the same memory
|
228
|
+
location. The result is a normalised SCS number.
|
229
|
+
*/
|
230
|
+
void scs_sub(scs_ptr result, scs_ptr x, scs_ptr y);
|
231
|
+
|
232
|
+
|
233
|
+
/** Addition without renormalisation, to be used for adding many
|
234
|
+
numbers.
|
235
|
+
@warning In case of a cancellation, severe loss of precision could
|
236
|
+
happen. Safe if the numbers are of the same sign.
|
237
|
+
*/
|
238
|
+
void scs_add_no_renorm(scs_ptr result, scs_ptr x, scs_ptr y);
|
239
|
+
|
240
|
+
|
241
|
+
/** Renormalisation (to be used after several scs_add_no_renorm).
|
242
|
+
This function removes the carry from each digit, and also shifts the
|
243
|
+
digits in case of a cancellation (so that if result != 0 then its
|
244
|
+
first digit is non-zero)
|
245
|
+
|
246
|
+
@warning THIS FUNCTION HAS NEVER BEEN PROPERLY TESTED and is
|
247
|
+
currently unused in the library: instead, specific renormalisation
|
248
|
+
steps are fused within the code of the operations which require it.
|
249
|
+
*/
|
250
|
+
|
251
|
+
void scs_renorm(scs_ptr);
|
252
|
+
|
253
|
+
|
254
|
+
/** Renormalisation assuming no cancellation. This function is useful
|
255
|
+
for example when adding many numbers of the same sign */
|
256
|
+
void scs_renorm_no_cancel_check(scs_ptr);
|
257
|
+
|
258
|
+
/**@}*/ /* end doxygen group for addition and normalisation functions*/
|
259
|
+
|
260
|
+
|
261
|
+
|
262
|
+
|
263
|
+
/* ****************************************************************** */
|
264
|
+
/**@name Multiplication functions */ /**@{*/
|
265
|
+
|
266
|
+
/** Multiplication of two SCS numbers. The arguments x, y and result
|
267
|
+
may point to the same memory location. The result is a normalised SCS
|
268
|
+
number.
|
269
|
+
*/
|
270
|
+
void scs_mul(scs_ptr result, const scs_ptr x, const scs_ptr y);
|
271
|
+
|
272
|
+
/** Multiplication of a SCS with an unsigned integer; result is
|
273
|
+
returned in x. */
|
274
|
+
void scs_mul_ui(scs_ptr, const unsigned int);
|
275
|
+
|
276
|
+
/** Square. Result is normalised */
|
277
|
+
void scs_square(scs_ptr result, scs_ptr x);
|
278
|
+
|
279
|
+
/** Fused multiply-and-add (ab+c); Result is normalised
|
280
|
+
\warning This function has not been tested thoroughly */
|
281
|
+
void scs_fma(scs_ptr result, scs_ptr a, scs_ptr b, scs_ptr c);
|
282
|
+
|
283
|
+
/**@}*/ /* end doxygen group for Multiplication functions*/
|
284
|
+
|
285
|
+
|
286
|
+
|
287
|
+
|
288
|
+
|
289
|
+
/* ****************************************************************** */
|
290
|
+
/**@name Divisions */ /**@{*/
|
291
|
+
|
292
|
+
/** SCS inverse.
|
293
|
+
Stores 1/x in result. Result is normalised
|
294
|
+
|
295
|
+
@warning This function is known not to work for most precisions: it
|
296
|
+
performs a fixed number of Newton-Raphson iterations (two), starting
|
297
|
+
with a FP number (53 bits), so provides roughly 210 bits of
|
298
|
+
precision. It should be modified to perform more iterations if more
|
299
|
+
precision is needed.
|
300
|
+
*/
|
301
|
+
void scs_inv(scs_ptr result, scs_ptr x);
|
302
|
+
|
303
|
+
/** SCS division. Computes x/y. Result is normalised
|
304
|
+
@warning This function is known not to work for most precisions: it
|
305
|
+
performs a fixed number of Newton-Raphson iterations (two), starting
|
306
|
+
with a FP number (53 bits), so provides roughly 210 bits of
|
307
|
+
precision. It should be modified to perform more iterations if more
|
308
|
+
precision is needed.
|
309
|
+
*/
|
310
|
+
void scs_div(scs_ptr result, scs_ptr x, scs_ptr y);
|
311
|
+
|
312
|
+
|
313
|
+
/** SCS division by 2. Computes x/2. Result is normalised */
|
314
|
+
void scs_div_2(scs_t x);
|
315
|
+
|
316
|
+
/**@}*/ /* end doxygen group for division functions*/
|
317
|
+
|
318
|
+
|
319
|
+
|
320
|
+
|
321
|
+
|
322
|
+
/* ****************************************************************** */
|
323
|
+
/**@name Functions for testing purpose */ /**@{*/
|
324
|
+
|
325
|
+
|
326
|
+
#ifdef HAVE_LIBGMP
|
327
|
+
/** Convert a SCS number into a GMP MPF (multiple precision,
|
328
|
+
floating-point) number. Should be exact if the target number has
|
329
|
+
more precision than the SCS number, otherwise the rounding is
|
330
|
+
unspecified (the conversion uses MPF functions) */
|
331
|
+
void scs_get_mpf(scs_ptr, mpf_t);
|
332
|
+
#endif
|
333
|
+
|
334
|
+
|
335
|
+
|
336
|
+
#ifdef HAVE_MPFR_H
|
337
|
+
/** Convert a SCS number into a MPFR (multiple precision,
|
338
|
+
floating-point) number. Should be exact if the target number has
|
339
|
+
more precision than the SCS number, otherwise should be correctly
|
340
|
+
rounded (the conversion uses MPFR functions). Not heavily tested
|
341
|
+
though */
|
342
|
+
void scs_get_mpfr(scs_ptr, mpfr_t);
|
343
|
+
#endif
|
344
|
+
|
345
|
+
|
346
|
+
/**@}*/ /* end doxygen group for functions for testing purpose */
|
347
|
+
|
348
|
+
#endif /* INCLUDE_SCS */
|
349
|
+
|
350
|
+
|
351
|
+
|
352
|
+
|
353
|
+
|