[CALC] Improve multi-precision support, and powers/roots. CORE-8486
[reactos.git] / base / applications / calc / utl_mpfr.c
1 /*
2 * ReactOS Calc (Utility functions for GMP/MPFR engine)
3 *
4 * Copyright 2007-2017, Carlo Bramini
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "calc.h"
22
23 void prepare_rpn_result_2(calc_number_t *rpn, TCHAR *buffer, int size, int base)
24 {
25 char temp[1024];
26 char *ptr, *dst;
27 int width, max_ld_width;
28 unsigned long int n, q;
29 mpz_t zz;
30 mpf_t ff;
31
32 mpz_init(zz);
33 mpf_init(ff);
34 mpfr_get_z(zz, rpn->mf, MPFR_DEFAULT_RND);
35 mpfr_get_f(ff, rpn->mf, MPFR_DEFAULT_RND);
36
37 switch (base) {
38 case IDC_RADIO_HEX:
39 gmp_sprintf(temp, "%ZX", zz);
40 break;
41 case IDC_RADIO_DEC:
42 /*
43 * The output display is much shorter in standard mode,
44 * so I'm forced to reduce the precision here :(
45 */
46 if (calc.layout == CALC_LAYOUT_STANDARD)
47 max_ld_width = 16;
48 else
49 max_ld_width = 64;
50
51 /* calculate the width of integer number */
52 if (mpf_sgn(ff) == 0)
53 width = 1;
54 else {
55 mpfr_t t;
56 mpfr_init(t);
57 mpfr_abs(t, rpn->mf, MPFR_DEFAULT_RND);
58 mpfr_log10(t, t, MPFR_DEFAULT_RND);
59 width = 1 + mpfr_get_si(t, MPFR_DEFAULT_RND);
60 mpfr_clear(t);
61 }
62 if (calc.sci_out == TRUE || width > max_ld_width || width < -max_ld_width)
63 ptr = temp + gmp_sprintf(temp, "%*.*#Fe", 1, max_ld_width, ff);
64 else {
65 ptr = temp + gmp_sprintf(temp, "%#*.*Ff", width, ((max_ld_width-width-1)>=0) ? max_ld_width-width-1 : 0, ff);
66 dst = strchr(temp, '.');
67 while (--ptr > dst)
68 if (*ptr != '0')
69 break;
70
71 /* put the string terminator for removing the final '0' (if any) */
72 ptr[1] = '\0';
73 /* check if the number finishes with '.' */
74 if (ptr == dst)
75 /* remove the dot (it will be re-added later) */
76 ptr[0] = '\0';
77 }
78 break;
79 case IDC_RADIO_OCT:
80 gmp_sprintf(temp, "%Zo", zz);
81 break;
82 case IDC_RADIO_BIN:
83 /* if the number is zero, just write 0 ;) */
84 if (rpn_is_zero(rpn)) {
85 temp[0] = _T('0');
86 temp[1] = _T('\0');
87 break;
88 }
89 /* repeat until a bit set to '1' is found */
90 n = 0;
91 do {
92 q = mpz_scan1(zz, n);
93 if (q == ULONG_MAX)
94 break;
95 while (n < q)
96 temp[n++] = '0';
97 temp[n++] = '1';
98 } while (1);
99 /* now revert the string into TCHAR buffer */
100 for (q=0; q<n; q++)
101 buffer[n-q-1] = (temp[q] == '1') ? _T('1') : _T('0');
102 buffer[n] = _T('\0');
103
104 mpz_clear(zz);
105 mpf_clear(ff);
106 return;
107 }
108 mpz_clear(zz);
109 mpf_clear(ff);
110 _sntprintf(buffer, SIZEOF(calc.buffer), _T("%hs"), temp);
111 }
112
113 void convert_text2number_2(calc_number_t *a)
114 {
115 int base;
116 #ifdef UNICODE
117 int sz;
118 char *temp;
119 #endif
120
121 switch (calc.base) {
122 case IDC_RADIO_HEX: base = 16; break;
123 case IDC_RADIO_DEC: base = 10; break;
124 case IDC_RADIO_OCT: base = 8; break;
125 case IDC_RADIO_BIN: base = 2; break;
126 default: return;
127 }
128 #ifdef UNICODE
129 /*
130 * libmpfr and libgmp accept only ascii chars.
131 */
132 sz = WideCharToMultiByte(CP_ACP, 0, calc.buffer, -1, NULL, 0, NULL, NULL);
133 if (!sz)
134 return;
135 temp = (char *)_alloca(sz);
136 sz = WideCharToMultiByte(CP_ACP, 0, calc.buffer, -1, temp, sz, NULL, NULL);
137 mpfr_strtofr(a->mf, temp, NULL, base, MPFR_DEFAULT_RND);
138 #else
139 mpfr_strtofr(a->mf, calc.buffer, NULL, base, MPFR_DEFAULT_RND);
140 #endif
141 }
142
143 void convert_real_integer(unsigned int base)
144 {
145 switch (base) {
146 case IDC_RADIO_DEC:
147 break;
148 case IDC_RADIO_OCT:
149 case IDC_RADIO_BIN:
150 case IDC_RADIO_HEX:
151 if (calc.base == IDC_RADIO_DEC) {
152 mpfr_trunc(calc.code.mf, calc.code.mf);
153 apply_int_mask(&calc.code);
154 }
155 break;
156 }
157 }