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