1 /*
2 Copyright (C) 2009, 2013 William Hart
3
4 This file is part of FLINT.
5
6 FLINT is free software: you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License (LGPL) as published
8 by the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version. See <https://www.gnu.org/licenses/>.
10 */
11
12 #include <stdlib.h>
13 #include <gmp.h>
14 #include "flint.h"
15 #include "fmpz.h"
16
17 #if FLINT_USES_PTHREAD
18 #include <pthread.h>
19
20 static pthread_once_t fmpz_initialised = PTHREAD_ONCE_INIT;
21 pthread_mutex_t fmpz_lock;
22 #endif
23
24 /* Always free larger mpz's to avoid wasting too much heap space */
25 #define FLINT_MPZ_MAX_CACHE_LIMBS 64
26
27 /* The number of new mpz's allocated at a time */
28 #define MPZ_BLOCK 64
29
30 /* there's no point using TLS here as GC doesn't support it */
31 __mpz_struct ** mpz_free_arr = NULL;
32 __mpz_struct ** mpz_arr = NULL;
33 ulong mpz_num = 0;
34 ulong mpz_alloc = 0;
35 ulong mpz_free_num = 0;
36 ulong mpz_free_alloc = 0;
37
38 #if FLINT_USES_PTHREAD
fmpz_lock_init()39 void fmpz_lock_init()
40 {
41 pthread_mutex_init(&fmpz_lock, NULL);
42 }
43 #endif
44
_fmpz_new_mpz(void)45 __mpz_struct * _fmpz_new_mpz(void)
46 {
47 __mpz_struct * z = NULL;
48
49 #if FLINT_USES_PTHREAD
50 pthread_once(&fmpz_initialised, fmpz_lock_init);
51 pthread_mutex_lock(&fmpz_lock);
52 #endif
53
54 if (mpz_free_num != 0)
55 z = mpz_free_arr[--mpz_free_num];
56 else
57 {
58 z = flint_malloc(sizeof(__mpz_struct));
59
60 if (mpz_num == mpz_alloc) /* store pointer to prevent gc cleanup */
61 {
62 mpz_alloc = FLINT_MAX(64, mpz_alloc * 2);
63 mpz_arr = flint_realloc(mpz_arr, mpz_alloc * sizeof(__mpz_struct *));
64 }
65 mpz_arr[mpz_num++] = z;
66
67 mpz_init(z);
68 }
69
70 #if FLINT_USES_PTHREAD
71 pthread_mutex_unlock(&fmpz_lock);
72 #endif
73
74 return z;
75 }
76
_fmpz_clear_mpz(fmpz f)77 void _fmpz_clear_mpz(fmpz f)
78 {
79 __mpz_struct * ptr = COEFF_TO_PTR(f);
80
81 if (ptr->_mp_alloc > FLINT_MPZ_MAX_CACHE_LIMBS)
82 mpz_realloc2(ptr, 1);
83
84 #if FLINT_USES_PTHREAD
85 pthread_mutex_lock(&fmpz_lock);
86 #endif
87
88 if (mpz_free_num == mpz_free_alloc)
89 {
90 mpz_free_alloc = FLINT_MAX(64, mpz_free_alloc * 2);
91 mpz_free_arr = flint_realloc(mpz_free_arr, mpz_free_alloc * sizeof(__mpz_struct *));
92 }
93
94 mpz_free_arr[mpz_free_num++] = ptr;
95
96 #if FLINT_USES_PTHREAD
97 pthread_mutex_unlock(&fmpz_lock);
98 #endif
99 }
100
_fmpz_cleanup_mpz_content(void)101 void _fmpz_cleanup_mpz_content(void)
102 {
103 ulong i;
104
105 for (i = 0; i < mpz_free_num; i++)
106 {
107 mpz_clear(mpz_free_arr[i]);
108 flint_free(mpz_free_arr[i]);
109 }
110
111 /* TODO: remove selected mpz's from mpz_arr too and compact */
112 mpz_free_num = mpz_free_alloc = 0;
113 }
114
_fmpz_cleanup(void)115 void _fmpz_cleanup(void)
116 {
117 #if FLINT_USES_PTHREAD
118 pthread_mutex_lock(&fmpz_lock);
119 #endif
120
121 _fmpz_cleanup_mpz_content();
122 flint_free(mpz_free_arr);
123 mpz_free_arr = NULL;
124
125 #if FLINT_USES_PTHREAD
126 pthread_mutex_unlock(&fmpz_lock);
127 #endif
128 }
129
_fmpz_promote(fmpz_t f)130 __mpz_struct * _fmpz_promote(fmpz_t f)
131 {
132 if (!COEFF_IS_MPZ(*f)) /* f is small so promote it first */
133 {
134 __mpz_struct * mpz_ptr = _fmpz_new_mpz();
135 (*f) = PTR_TO_COEFF(mpz_ptr);
136 return mpz_ptr;
137 }
138 else /* f is large already, just return the pointer */
139 return COEFF_TO_PTR(*f);
140 }
141
_fmpz_promote_val(fmpz_t f)142 __mpz_struct * _fmpz_promote_val(fmpz_t f)
143 {
144 fmpz c = (*f);
145 if (!COEFF_IS_MPZ(c)) /* f is small so promote it */
146 {
147 __mpz_struct * mpz_ptr = _fmpz_new_mpz();
148 (*f) = PTR_TO_COEFF(mpz_ptr);
149 flint_mpz_set_si(mpz_ptr, c);
150 return mpz_ptr;
151 }
152 else /* f is large already, just return the pointer */
153 return COEFF_TO_PTR(c);
154 }
155
_fmpz_demote_val(fmpz_t f)156 void _fmpz_demote_val(fmpz_t f)
157 {
158 __mpz_struct * mpz_ptr = COEFF_TO_PTR(*f);
159 int size = mpz_ptr->_mp_size;
160
161 if (size == 1 || size == -1)
162 {
163 ulong uval = mpz_ptr->_mp_d[0];
164
165 if (uval <= (ulong) COEFF_MAX)
166 {
167 _fmpz_clear_mpz(*f);
168 *f = size * (fmpz) uval;
169 }
170 }
171 else if (size == 0) /* value is 0 */
172 {
173 _fmpz_clear_mpz(*f);
174 *f = 0;
175 }
176
177 /* don't do anything if value has to be multi precision */
178 }
179
_fmpz_init_readonly_mpz(fmpz_t f,const mpz_t z)180 void _fmpz_init_readonly_mpz(fmpz_t f, const mpz_t z)
181 {
182 __mpz_struct *ptr;
183 *f = WORD(0);
184 ptr = _fmpz_promote(f);
185
186 mpz_clear(ptr);
187 *ptr = *z;
188 }
189
_fmpz_clear_readonly_mpz(mpz_t z)190 void _fmpz_clear_readonly_mpz(mpz_t z)
191 {
192 if (((z->_mp_size == 1 || z->_mp_size == -1) && (z->_mp_d[0] <= COEFF_MAX))
193 || (z->_mp_size == 0))
194 {
195 mpz_clear(z);
196 }
197
198 }
199