1/*
2 * Copyright (c) 2008-2020 Stefan Krah. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28
29#include "mpdecimal.h"
30
31#include <signal.h>
32#include <stdio.h>
33#include <string.h>
34
35
36void
37mpd_dflt_traphandler(mpd_context_t *ctx)
38{
39    (void)ctx;
40    raise(SIGFPE);
41}
42
43void (* mpd_traphandler)(mpd_context_t *) = mpd_dflt_traphandler;
44
45
46/* Set guaranteed minimum number of coefficient words. The function may
47   be used once at program start. Setting MPD_MINALLOC to out-of-bounds
48   values is a catastrophic error, so in that case the function exits rather
49   than relying on the user to check a return value. */
50void
51mpd_setminalloc(mpd_ssize_t n)
52{
53    static int minalloc_is_set = 0;
54
55    if (minalloc_is_set) {
56        mpd_err_warn("mpd_setminalloc: ignoring request to set "
57                     "MPD_MINALLOC a second time\n");
58        return;
59    }
60    if (n < MPD_MINALLOC_MIN || n > MPD_MINALLOC_MAX) {
61        mpd_err_fatal("illegal value for MPD_MINALLOC"); /* GCOV_NOT_REACHED */
62    }
63    MPD_MINALLOC = n;
64    minalloc_is_set = 1;
65}
66
67void
68mpd_init(mpd_context_t *ctx, mpd_ssize_t prec)
69{
70    mpd_ssize_t ideal_minalloc;
71
72    mpd_defaultcontext(ctx);
73
74    if (!mpd_qsetprec(ctx, prec)) {
75        mpd_addstatus_raise(ctx, MPD_Invalid_context);
76        return;
77    }
78
79    ideal_minalloc = 2 * ((prec+MPD_RDIGITS-1) / MPD_RDIGITS);
80    if (ideal_minalloc < MPD_MINALLOC_MIN) ideal_minalloc = MPD_MINALLOC_MIN;
81    if (ideal_minalloc > MPD_MINALLOC_MAX) ideal_minalloc = MPD_MINALLOC_MAX;
82
83    mpd_setminalloc(ideal_minalloc);
84}
85
86void
87mpd_maxcontext(mpd_context_t *ctx)
88{
89    ctx->prec=MPD_MAX_PREC;
90    ctx->emax=MPD_MAX_EMAX;
91    ctx->emin=MPD_MIN_EMIN;
92    ctx->round=MPD_ROUND_HALF_EVEN;
93    ctx->traps=MPD_Traps;
94    ctx->status=0;
95    ctx->newtrap=0;
96    ctx->clamp=0;
97    ctx->allcr=1;
98}
99
100void
101mpd_defaultcontext(mpd_context_t *ctx)
102{
103    ctx->prec=2*MPD_RDIGITS;
104    ctx->emax=MPD_MAX_EMAX;
105    ctx->emin=MPD_MIN_EMIN;
106    ctx->round=MPD_ROUND_HALF_UP;
107    ctx->traps=MPD_Traps;
108    ctx->status=0;
109    ctx->newtrap=0;
110    ctx->clamp=0;
111    ctx->allcr=1;
112}
113
114void
115mpd_basiccontext(mpd_context_t *ctx)
116{
117    ctx->prec=9;
118    ctx->emax=MPD_MAX_EMAX;
119    ctx->emin=MPD_MIN_EMIN;
120    ctx->round=MPD_ROUND_HALF_UP;
121    ctx->traps=MPD_Traps|MPD_Clamped;
122    ctx->status=0;
123    ctx->newtrap=0;
124    ctx->clamp=0;
125    ctx->allcr=1;
126}
127
128int
129mpd_ieee_context(mpd_context_t *ctx, int bits)
130{
131    if (bits <= 0 || bits > MPD_IEEE_CONTEXT_MAX_BITS || bits % 32) {
132        return -1;
133    }
134
135    ctx->prec = 9 * (bits/32) - 2;
136    ctx->emax = 3 * ((mpd_ssize_t)1<<(bits/16+3));
137    ctx->emin = 1 - ctx->emax;
138    ctx->round=MPD_ROUND_HALF_EVEN;
139    ctx->traps=0;
140    ctx->status=0;
141    ctx->newtrap=0;
142    ctx->clamp=1;
143    ctx->allcr=1;
144
145    return 0;
146}
147
148mpd_ssize_t
149mpd_getprec(const mpd_context_t *ctx)
150{
151    return ctx->prec;
152}
153
154mpd_ssize_t
155mpd_getemax(const mpd_context_t *ctx)
156{
157    return ctx->emax;
158}
159
160mpd_ssize_t
161mpd_getemin(const mpd_context_t *ctx)
162{
163    return ctx->emin;
164}
165
166int
167mpd_getround(const mpd_context_t *ctx)
168{
169    return ctx->round;
170}
171
172uint32_t
173mpd_gettraps(const mpd_context_t *ctx)
174{
175    return ctx->traps;
176}
177
178uint32_t
179mpd_getstatus(const mpd_context_t *ctx)
180{
181    return ctx->status;
182}
183
184int
185mpd_getclamp(const mpd_context_t *ctx)
186{
187    return ctx->clamp;
188}
189
190int
191mpd_getcr(const mpd_context_t *ctx)
192{
193    return ctx->allcr;
194}
195
196
197int
198mpd_qsetprec(mpd_context_t *ctx, mpd_ssize_t prec)
199{
200    if (prec <= 0 || prec > MPD_MAX_PREC) {
201        return 0;
202    }
203    ctx->prec = prec;
204    return 1;
205}
206
207int
208mpd_qsetemax(mpd_context_t *ctx, mpd_ssize_t emax)
209{
210    if (emax < 0 || emax > MPD_MAX_EMAX) {
211        return 0;
212    }
213    ctx->emax = emax;
214    return 1;
215}
216
217int
218mpd_qsetemin(mpd_context_t *ctx, mpd_ssize_t emin)
219{
220    if (emin > 0 || emin < MPD_MIN_EMIN) {
221        return 0;
222    }
223    ctx->emin = emin;
224    return 1;
225}
226
227int
228mpd_qsetround(mpd_context_t *ctx, int round)
229{
230    if (!(0 <= round && round < MPD_ROUND_GUARD)) {
231        return 0;
232    }
233    ctx->round = round;
234    return 1;
235}
236
237int
238mpd_qsettraps(mpd_context_t *ctx, uint32_t flags)
239{
240    if (flags > MPD_Max_status) {
241        return 0;
242    }
243    ctx->traps = flags;
244    return 1;
245}
246
247int
248mpd_qsetstatus(mpd_context_t *ctx, uint32_t flags)
249{
250    if (flags > MPD_Max_status) {
251        return 0;
252    }
253    ctx->status = flags;
254    return 1;
255}
256
257int
258mpd_qsetclamp(mpd_context_t *ctx, int c)
259{
260    if (c != 0 && c != 1) {
261        return 0;
262    }
263    ctx->clamp = c;
264    return 1;
265}
266
267int
268mpd_qsetcr(mpd_context_t *ctx, int c)
269{
270    if (c != 0 && c != 1) {
271        return 0;
272    }
273    ctx->allcr = c;
274    return 1;
275}
276
277
278void
279mpd_addstatus_raise(mpd_context_t *ctx, uint32_t flags)
280{
281    ctx->status |= flags;
282    if (flags&ctx->traps) {
283        ctx->newtrap = (flags&ctx->traps);
284        mpd_traphandler(ctx);
285    }
286}
287