xref: /dragonfly/contrib/mpc/src/get_x.c (revision 73610d44)
1 /* mpc_get_dc, mpc_get_ldc -- Transform mpc number into C complex number
2    mpc_get_str -- Convert a complex number into a string.
3 
4 Copyright (C) 2009, 2010, 2011 INRIA
5 
6 This file is part of GNU MPC.
7 
8 GNU MPC is free software; you can redistribute it and/or modify it under
9 the terms of the GNU Lesser General Public License as published by the
10 Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12 
13 GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
16 more details.
17 
18 You should have received a copy of the GNU Lesser General Public License
19 along with this program. If not, see http://www.gnu.org/licenses/ .
20 */
21 
22 #include "config.h"
23 
24 #ifdef HAVE_COMPLEX_H
25 #include <complex.h>
26 #endif
27 
28 #ifdef HAVE_LOCALE_H
29 #include <locale.h>
30 #endif
31 
32 #include <stdio.h> /* for sprintf, fprintf */
33 #include <ctype.h>
34 #include <string.h>
35 #include "mpc-impl.h"
36 
37 #ifdef HAVE_COMPLEX_H
38 double _Complex
39 mpc_get_dc (mpc_srcptr op, mpc_rnd_t rnd) {
40    return I * mpfr_get_d (mpc_imagref (op), MPC_RND_IM (rnd))
41           + mpfr_get_d (mpc_realref (op), MPC_RND_RE (rnd));
42 }
43 
44 long double _Complex
45 mpc_get_ldc (mpc_srcptr op, mpc_rnd_t rnd) {
46    return I * mpfr_get_ld (mpc_imagref (op), MPC_RND_IM (rnd))
47           + mpfr_get_ld (mpc_realref (op), MPC_RND_RE (rnd));
48 }
49 #endif
50 
51 
52 /* Code for mpc_get_str. The output format is "(real imag)", the decimal point
53    of the locale is used. */
54 
55 /* mpfr_prec_t can be either int or long int */
56 #if (__GMP_MP_SIZE_T_INT == 1)
57 #define MPC_EXP_FORMAT_SPEC "i"
58 #elif (__GMP_MP_SIZE_T_INT == 0)
59 #define MPC_EXP_FORMAT_SPEC "li"
60 #else
61 #error "mpfr_exp_t size not supported"
62 #endif
63 
64 static char *
65 pretty_zero (mpfr_srcptr zero)
66 {
67   char *pretty;
68 
69   pretty = mpc_alloc_str (3);
70 
71   pretty[0] = mpfr_signbit (zero) ? '-' : '+';
72   pretty[1] = '0';
73   pretty[2] = '\0';
74 
75   return pretty;
76 }
77 
78 static char *
79 prettify (const char *str, const mp_exp_t expo, int base, int special)
80 {
81   size_t sz;
82   char *pretty;
83   char *p;
84   const char *s;
85   mp_exp_t x;
86   int sign;
87 
88   sz = strlen (str) + 1; /* + terminal '\0' */
89 
90   if (special)
91     {
92       /* special number: nan or inf */
93       pretty = mpc_alloc_str (sz);
94       strcpy (pretty, str);
95 
96       return pretty;
97     }
98 
99   /* regular number */
100 
101   sign = (str[0] == '-' || str[0] == '+');
102 
103   x = expo - 1; /* expo is the exponent value with decimal point BEFORE
104                    the first digit, we wants decimal point AFTER the first
105                    digit */
106   if (base == 16)
107     x <<= 2; /* the output exponent is a binary exponent */
108 
109   ++sz; /* + decimal point */
110 
111   if (x != 0)
112     {
113       /* augment sz with the size needed for an exponent written in base
114          ten */
115       mp_exp_t xx;
116 
117       sz += 3; /* + exponent char + sign + 1 digit */
118 
119       if (x < 0)
120         {
121           /* avoid overflow when changing sign (assuming that, for the
122              mp_exp_t type, (max value) is greater than (- min value / 10)) */
123           if (x < -10)
124             {
125               xx = - (x / 10);
126               sz++;
127             }
128           else
129             xx = -x;
130         }
131       else
132         xx = x;
133 
134       /* compute sz += floor(log(expo)/log(10)) without using libm
135          functions */
136       while (xx > 9)
137         {
138           sz++;
139           xx /= 10;
140         }
141     }
142 
143   pretty = mpc_alloc_str (sz);
144   p = pretty;
145 
146   /* 1. optional sign plus first digit */
147   s = str;
148   *p++ = *s++;
149   if (sign)
150     *p++ = *s++;
151 
152   /* 2. decimal point */
153 #ifdef HAVE_LOCALECONV
154   *p++ = *localeconv ()->decimal_point;
155 #else
156   *p++ = '.';
157 #endif
158   *p = '\0';
159 
160   /* 3. other significant digits */
161   strcat (pretty, s);
162 
163   /* 4. exponent (in base ten) */
164   if (x == 0)
165     return pretty;
166 
167   p = pretty + strlen (str) + 1;
168 
169   switch (base)
170     {
171     case 10:
172       *p++ = 'e';
173       break;
174     case 2:
175     case 16:
176       *p++ = 'p';
177       break;
178     default:
179       *p++ = '@';
180     }
181 
182   *p = '\0';
183 
184   sprintf (p, "%+"MPC_EXP_FORMAT_SPEC, x);
185 
186   return pretty;
187 }
188 
189 static char *
190 get_pretty_str (const int base, const size_t n, mpfr_srcptr x, mpfr_rnd_t rnd)
191 {
192   mp_exp_t expo;
193   char *ugly;
194   char *pretty;
195 
196   if (mpfr_zero_p (x))
197     return pretty_zero (x);
198 
199   ugly = mpfr_get_str (NULL, &expo, base, n, x, rnd);
200   MPC_ASSERT (ugly != NULL);
201   pretty = prettify (ugly, expo, base, !mpfr_number_p (x));
202   mpfr_free_str (ugly);
203 
204   return pretty;
205 }
206 
207 char *
208 mpc_get_str (int base, size_t n, mpc_srcptr op, mpc_rnd_t rnd)
209 {
210   size_t needed_size;
211   char *real_str;
212   char *imag_str;
213   char *complex_str = NULL;
214 
215   if (base < 2 || base > 36)
216     return NULL;
217 
218   real_str = get_pretty_str (base, n, mpc_realref (op), MPC_RND_RE (rnd));
219   imag_str = get_pretty_str (base, n, mpc_imagref (op), MPC_RND_IM (rnd));
220 
221   needed_size = strlen (real_str) + strlen (imag_str) + 4;
222 
223   complex_str = mpc_alloc_str (needed_size);
224 MPC_ASSERT (complex_str != NULL);
225 
226   strcpy (complex_str, "(");
227   strcat (complex_str, real_str);
228   strcat (complex_str, " ");
229   strcat (complex_str, imag_str);
230   strcat (complex_str, ")");
231 
232   mpc_free_str (real_str);
233   mpc_free_str (imag_str);
234 
235   return complex_str;
236 }
237