]> git.llucax.com Git - z.facultad/75.29/dale.git/blob - src/number.h
7df04e172b9bb81511748c9a9097a8f903b456da
[z.facultad/75.29/dale.git] / src / number.h
1 #ifdef _WIN32
2 // min y max entran en conflicto con la windows.h, son rebautizadas en Windows
3 #define min _cpp_min
4 #define max _cpp_max
5 #endif
6
7 #include <deque>
8 #include <utility>
9 #include <algorithm>
10 #include <stdint.h>
11
12 /* sizeof(E) tiene que ser 2*sizeof(N); y son los tipos nativos con los cuales
13  * se haran las operaciones mas basicas. */
14
15 template < typename N, typename E >
16 struct number;
17
18 template < typename N, typename E >
19 std::ostream& operator<< (std::ostream& os, const number< N, E >& n);
20
21 template < typename N = uint32_t, typename E = uint64_t >
22 struct number
23 {
24
25         // Tipos
26         typedef N native_type;
27         typedef E extended_type;
28         enum sign_type { positive, negative };
29         typedef typename std::deque< native_type > chunk_type;
30         typedef typename chunk_type::size_type size_type;
31         typedef typename chunk_type::iterator iterator;
32         typedef typename chunk_type::const_iterator const_iterator;
33         typedef typename chunk_type::reverse_iterator reverse_iterator;
34         typedef typename chunk_type::const_reverse_iterator const_reverse_iterator;
35
36         // Constructores (después de construído, el chunk siempre tiene al
37         // menos un elemento).
38         // Constructor default (1 'átomo con valor 0)
39         number(): chunk(1, 0) {}
40
41         // Constructor a partir de buffer (de 'átomos') y tamaño
42         // Copia cada elemento del buffer como un 'átomo' del chunk
43         // (el átomo menos significativo es el chunk[0] == buf[0])
44         number(native_type* buf, size_type len, sign_type sign = positive):
45                 chunk(buf, buf + len), sign(sign)
46         {
47                 fix_empty();
48         }
49
50         // Constructor a partir de un 'átomo' (lo asigna como único elemento
51         // del chunk). Copia una vez N en el vector.
52         number(native_type n, sign_type sign = positive):
53                 chunk(1, n), sign(sign) {}
54
55         // TODO constructor a partir de string.
56
57         // Operadores
58         number& operator++ ()
59         {
60                 carry(0);
61                 return *this;
62         }
63
64         number& operator+= (const number& n);
65         number& operator*= (const number& n);
66         number& operator<<= (const size_type n);
67
68         // Devuelve referencia a 'átomo' i del chunk (no debería ser necesario
69         // si la multiplicación es un método de este objeto).
70         native_type& operator[] (size_type i) {
71                 return chunk[i];
72         }
73
74         // Iteradores (no deberían ser necesarios)
75         iterator begin() { return chunk.begin(); }
76         iterator end() { return chunk.end(); }
77         const_iterator begin() const { return chunk.begin(); }
78         const_iterator end() const { return chunk.end(); }
79         reverse_iterator rbegin() { return chunk.rbegin(); }
80         reverse_iterator rend() { return chunk.rend(); }
81         const_reverse_iterator rbegin() const { return chunk.rbegin(); }
82         const_reverse_iterator rend() const { return chunk.rend(); }
83
84         // Friends
85         template < typename NN, typename EE >
86         friend std::ostream& operator<< (std::ostream& os, const number< NN, EE>& n);
87
88         private:
89         // Atributos
90         chunk_type chunk;
91         sign_type sign;
92
93         // Helpers
94         // Normaliza las longitudes de 2 numbers, completando con 0s a la izquierda
95         // al más pequeño. Sirve para División y Conquista
96         number& normalize_length(const number& n);
97         // parte un número en dos mitades de misma longitud, devuelve un par de
98         // números con (low, high)
99         std::pair< number, number > split() const;
100         // Pone un chunk en 0 para que sea un invariante de representación que
101         // el chunk no sea vacío (siempre tenga la menos un elemento).
102         void fix_empty() { if (!chunk.size()) chunk.push_back(0); }
103         // Propaga carry a partir del 'átomo' i (suma 1 al 'átomo' i propagando
104         // carry)
105         void carry(size_type i)
106         {
107                 if (chunk.size() > i)
108                 {
109                         ++chunk[i];
110                         if (chunk[i] == 0)
111                                 carry(i+1); // Overflow
112                 }
113                 else
114                         chunk.push_back(1);
115         }
116
117 };
118
119 template < typename N, typename E >
120 number< N, E >& number< N, E >::operator+= (const number< N, E >& n)
121 {
122         native_type c = 0;
123         size_type ini = 0;
124         size_type fin = std::min(chunk.size(), n.chunk.size());
125         size_type i; //problema de VC++, da error de redefinición
126
127         // "intersección" entre ambos chunks
128         // +-----+-----+------+------+
129         // |     |     |      |      | <--- mio
130         // +-----+-----+------+------+
131         // +-----+-----+------+
132         // |     |     |      |        <--- chunk de n
133         // +-----+-----+------+
134         //
135         // |------------------|
136         // Esto se procesa en este for
137         for (i = ini; i < fin; ++i)
138         {
139                 chunk[i] += n.chunk[i] + c;
140                 if ((chunk[i] < n.chunk[i]) || \
141                                 ( (n.chunk[i] == 0) && c && (chunk[i] == 0) ))
142                         c = 1; // Overflow
143                 else
144                         c = 0; // OK
145         }
146
147         // si mi chunk es más grande que el del otro, sólo me queda
148         // propagar el carry
149         if (chunk.size() >= n.chunk.size())
150         {
151                 if (c)
152                         carry(fin); // Propago carry
153                 return *this;
154         }
155
156         // Hay más
157         // +-----+-----+------+
158         // |     |     |      |         <--- mío
159         // +-----+-----+------+
160         // +-----+-----+------+------+
161         // |     |     |      |      |  <--- chunk de n
162         // +-----+-----+------+------+
163         //
164         //                    |------|
165         //            Esto se procesa en este for
166         // (suma los chunks de n propagando algún carry si lo había)
167         ini = fin;
168         fin = n.chunk.size();
169         for (i = ini; i < fin; ++i)
170         {
171                 chunk.push_back(n.chunk[i] + c); // Agrego nuevo átomo
172                 if (chunk[i] != 0 || !c)
173                         c = 0; // OK
174                 else
175                         c = 1; // Overflow
176         }
177
178         // Si me queda algún carry colgado, hay que agregar un "átomo"
179         // más al chunk.
180         if (c)
181                 chunk.push_back(1); // Último carry
182
183         return *this;
184 }
185
186 template < typename N, typename E >
187 number< N, E > operator+ (const number< N, E >& n1, const number< N, E >& n2)
188 {
189         number< N, E > tmp = n1;
190         tmp += n2;
191         return tmp;
192 }
193
194 // efectúa un shifteo a izquierda del chunk, agregando 0s en los casilleros menos significativos
195 template < typename N, typename E >
196 number< N, E >& number< N, E >::operator<<= (size_type n)
197 {
198         size_type i;
199         for (i = 0; i < n; i++)
200         {
201                 chunk.push_front(0);
202         }
203         return *this;
204 }
205
206 template < typename N, typename E >
207 number< N, E > operator<< (const number< N, E >& n, typename number< N, E >::size_type m)
208 {
209         number< N, E > tmp = n;
210         tmp <<= m;
211         return tmp;
212 }
213
214 template < typename N, typename E >
215 std::ostream& operator<< (std::ostream& os, const number< N, E >& n)
216 {
217         // FIXME sacar una salida bonita en ASCII =)
218         for (typename number< N, E >::const_iterator i = n.chunk.begin();
219                         i != n.chunk.end(); ++i)
220                 os << std::hex << *i << " ";
221         return os;
222 }
223
224 template < typename N, typename E >
225 number< N, E >& number< N, E >::operator*= (const number< N, E >& n)
226 {
227         number < N, E > r_op = n;
228         normalize_length(n);
229         n.normalize_length(*this);
230         *this = divide_n_conquer(*this, n);
231         return *this;
232 }
233
234 template < typename N, typename E >
235 number< N, E > operator* (const number< N, E >& n1, const number< N, E >& n2)
236 {
237         number< N, E > tmp = n1;
238         tmp *= n2;
239         return tmp;
240 }
241
242 template < typename N, typename E >
243 number< N, E >& number< N, E >::normalize_length(const number< N, E >& n)
244 {
245         // si son de distinto tamaño tengo que agregar ceros a la izquierda al
246         // menor para división y conquista
247         while (chunk.size() < n.chunk.size())
248         {
249                 chunk.push_back(0);
250         }
251
252         // si no tiene cantidad par de números le agrego un atomic_type 0 a la
253         // izquierda para no tener que contemplar splits de chunks impares
254         if ((chunk.size() % 2) != 0)
255         {
256                 chunk.push_back(0);
257         }
258 }
259
260 template < typename N, typename E >
261 std::pair< number< N, E >, number< N, E > > number< N, E >::split() const
262 {
263         typedef number< N, E > num_type;
264         typename num_type::size_type full_size = chunk.size();
265         typename num_type::size_type halves_size = full_size / 2;
266         typename num_type::size_type i = 0;
267
268         // vacío las mitades
269         std::pair< num_type, num_type > par;
270
271         // la primera mitad va al pedazo inferior
272         for (i = 0; i < halves_size; i++)
273         {
274                 par.first.chunk.push_back(chunk[i]);
275         }
276
277         // la segunda mitad (si full_size es impar es 1 más que la primera
278         // mitad) va al pedazo superior
279         for ( ; i < full_size; i++)
280         {
281                 par.second.chunk.push_back(chunk[i]);
282         }
283         return par;
284 }
285
286 // es el algoritmo de división y conquista, que se llama recursivamente
287 template < typename N, typename E >
288 number < N, E > karatsuba(number< N, E > u, number< N, E > v)
289 {
290         typedef number< N, E > num_type;
291
292         // tomo el chunk size de u (el de v DEBE ser el mismo)
293         typename num_type::size_type chunk_size = u.chunk.size();
294
295         if (chunk_size == 1)
296         {
297                 // condición de corte. Ver que por más que tenga 1 único
298                 // elemento puede "rebalsar" la capacidad del atomic_type,
299                 // como ser multiplicando 0xff * 0xff usando bytes!!!
300                 return u.chunk[0] * v.chunk[0];
301         }
302
303         std::pair< num_type, num_type > u12 = u.split();
304         std::pair< num_type, num_type > v12 = v.split();
305
306         // Los nombres M, D y H los puso Rosita en clase, cambiar si se les
307         // ocurren algunos mejores!
308         // m = u1*v1
309         // d = u2*v2
310         // h = (u1+v1)*(u2+v2) = u1*u2+u1*v2+u2*v1+u2*v2
311         num_type m = karastuba(u12.first, v12.first);
312         num_type d = karastuba(u12.second, v12.second);
313         num_type h = karastuba(u12.first + v12.first,
314                         u12.second + v12.second);
315
316         // H-D-M = u1*u2+u1*v2+u2*v1+u2*v2 - u2*v2 - u1*v1 = u1*v2+u2*v1
317         // u1*v1 << base^N + u1*v2+u2*v1 << base^N/2 + u2*v2
318         return (m << chunk_size) + ((h - d - m) << chunk_size / 2) + h;
319
320 }
321