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