1/*
2 * Copyright (c) 2008-2016 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#include <stdio.h>
31#include <string.h>
32#include <signal.h>
33
34
35void
36mpd_dflt_traphandler(mpd_context_t *ctx UNUSED)
37{
38    raise(SIGFPE);
39}
40
41void (* mpd_traphandler)(mpd_context_t *) = mpd_dflt_traphandler;
42
43
44/* Set guaranteed minimum number of coefficient words. The function may
45   be used once at program start. Setting MPD_MINALLOC to out-of-bounds
46   values is a catastrophic error, so in that case the function exits rather
47   than relying on the user to check a return value. */
48void
49mpd_setminalloc(mpd_ssize_t n)
50{
51    static int minalloc_is_set = 0;
52
53    if (minalloc_is_set) {
54        mpd_err_warn("mpd_setminalloc: ignoring request to set "
55                     "MPD_MINALLOC a second time\n");
56        return;
57    }
58    if (n < MPD_MINALLOC_MIN || n > MPD_MINALLOC_MAX) {
59        mpd_err_fatal("illegal value for MPD_MINALLOC"); /* GCOV_NOT_REACHED */
60    }
61    MPD_MINALLOC = n;
62    minalloc_is_set = 1;
63}
64
65void
66mpd_init(mpd_context_t *ctx, mpd_ssize_t prec)
67{
68    mpd_ssize_t ideal_minalloc;
69
70    mpd_defaultcontext(ctx);
71
72    if (!mpd_qsetprec(ctx, prec)) {
73        mpd_addstatus_raise(ctx, MPD_Invalid_context);
74        return;
75    }
76
77    ideal_minalloc = 2 * ((prec+MPD_RDIGITS-1) / MPD_RDIGITS);
78    if (ideal_minalloc < MPD_MINALLOC_MIN) ideal_minalloc = MPD_MINALLOC_MIN;
79    if (ideal_minalloc > MPD_MINALLOC_MAX) ideal_minalloc = MPD_MINALLOC_MAX;
80
81    mpd_setminalloc(ideal_minalloc);
82}
83
84void
85mpd_maxcontext(mpd_context_t *ctx)
86{
87    ctx->prec=MPD_MAX_PREC;
88    ctx->emax=MPD_MAX_EMAX;
89    ctx->emin=MPD_MIN_EMIN;
90    ctx->round=MPD_ROUND_HALF_EVEN;
91    ctx->traps=MPD_Traps;
92    ctx->status=0;
93    ctx->newtrap=0;
94    ctx->clamp=0;
95    ctx->allcr=1;
96}
97
98void
99mpd_defaultcontext(mpd_context_t *ctx)
100{
101    ctx->prec=2*MPD_RDIGITS;
102    ctx->emax=MPD_MAX_EMAX;
103    ctx->emin=MPD_MIN_EMIN;
104    ctx->round=MPD_ROUND_HALF_UP;
105    ctx->traps=MPD_Traps;
106    ctx->status=0;
107    ctx->newtrap=0;
108    ctx->clamp=0;
109    ctx->allcr=1;
110}
111
112void
113mpd_basiccontext(mpd_context_t *ctx)
114{
115    ctx->prec=9;
116    ctx->emax=MPD_MAX_EMAX;
117    ctx->emin=MPD_MIN_EMIN;
118    ctx->round=MPD_ROUND_HALF_UP;
119    ctx->traps=MPD_Traps|MPD_Clamped;
120    ctx->status=0;
121    ctx->newtrap=0;
122    ctx->clamp=0;
123    ctx->allcr=1;
124}
125
126int
127mpd_ieee_context(mpd_context_t *ctx, int bits)
128{
129    if (bits <= 0 || bits > MPD_IEEE_CONTEXT_MAX_BITS || bits % 32) {
130        return -1;
131    }
132
133    ctx->prec = 9 * (bits/32) - 2;
134    ctx->emax = 3 * ((mpd_ssize_t)1<<(bits/16+3));
135    ctx->emin = 1 - ctx->emax;
136    ctx->round=MPD_ROUND_HALF_EVEN;
137    ctx->traps=0;
138    ctx->status=0;
139    ctx->newtrap=0;
140    ctx->clamp=1;
141    ctx->allcr=1;
142
143    return 0;
144}
145
146mpd_ssize_t
147mpd_getprec(const mpd_context_t *ctx)
148{
149    return ctx->prec;
150}
151
152mpd_ssize_t
153mpd_getemax(const mpd_context_t *ctx)
154{
155    return ctx->emax;
156}
157
158mpd_ssize_t
159mpd_getemin(const mpd_context_t *ctx)
160{
161    return ctx->emin;
162}
163
164int
165mpd_getround(const mpd_context_t *ctx)
166{
167    return ctx->round;
168}
169
170uint32_t
171mpd_gettraps(const mpd_context_t *ctx)
172{
173    return ctx->traps;
174}
175
176uint32_t
177mpd_getstatus(const mpd_context_t *ctx)
178{
179    return ctx->status;
180}
181
182int
183mpd_getclamp(const mpd_context_t *ctx)
184{
185    return ctx->clamp;
186}
187
188int
189mpd_getcr(const mpd_context_t *ctx)
190{
191    return ctx->allcr;
192}
193
194
195int
196mpd_qsetprec(mpd_context_t *ctx, mpd_ssize_t prec)
197{
198    if (prec <= 0 || prec > MPD_MAX_PREC) {
199        return 0;
200    }
201    ctx->prec = prec;
202    return 1;
203}
204
205int
206mpd_qsetemax(mpd_context_t *ctx, mpd_ssize_t emax)
207{
208    if (emax < 0 || emax > MPD_MAX_EMAX) {
209        return 0;
210    }
211    ctx->emax = emax;
212    return 1;
213}
214
215int
216mpd_qsetemin(mpd_context_t *ctx, mpd_ssize_t emin)
217{
218    if (emin > 0 || emin < MPD_MIN_EMIN) {
219        return 0;
220    }
221    ctx->emin = emin;
222    return 1;
223}
224
225int
226mpd_qsetround(mpd_context_t *ctx, int round)
227{
228    if (!(0 <= round && round < MPD_ROUND_GUARD)) {
229        return 0;
230    }
231    ctx->round = round;
232    return 1;
233}
234
235int
236mpd_qsettraps(mpd_context_t *ctx, uint32_t traps)
237{
238    if (traps > MPD_Max_status) {
239        return 0;
240    }
241    ctx->traps = traps;
242    return 1;
243}
244
245int
246mpd_qsetstatus(mpd_context_t *ctx, uint32_t flags)
247{
248    if (flags > MPD_Max_status) {
249        return 0;
250    }
251    ctx->status = flags;
252    return 1;
253}
254
255int
256mpd_qsetclamp(mpd_context_t *ctx, int c)
257{
258    if (c != 0 && c != 1) {
259        return 0;
260    }
261    ctx->clamp = c;
262    return 1;
263}
264
265int
266mpd_qsetcr(mpd_context_t *ctx, int c)
267{
268    if (c != 0 && c != 1) {
269        return 0;
270    }
271    ctx->allcr = c;
272    return 1;
273}
274
275
276void
277mpd_addstatus_raise(mpd_context_t *ctx, uint32_t flags)
278{
279    ctx->status |= flags;
280    if (flags&ctx->traps) {
281        ctx->newtrap = (flags&ctx->traps);
282        mpd_traphandler(ctx);
283    }
284}
285
286
287