1 #include "sys.h"
2 #include "debug.h"
3 #include <iostream>
4 #include <string>
5 #include <cmath>
6 #include <iomanip>
7 #include <boost/numeric/ublas/matrix.hpp>
8 #include <boost/numeric/ublas/io.hpp>
9 #include <gmpxx.h>
10 #include "MultiLoop.h"
11 #include "utils.h"
12
13 using std::cout;
14 using std::endl;
15 using std::flush;
16
17 typedef unsigned long elements_type; // The type of an elements of GF(2^n) count.
18 typedef unsigned long fecs_type; // The type of an FEC of GF(2^n) count.
19 typedef std::vector<unsigned long> input_type; // The type of the vector with the input sequence.
20
21 std::string program_name; // The name of the program (argv[0]).
22
23 // This functor translates comma's to spaces.
24 struct CommaToSpace {
operator ()CommaToSpace25 char operator()(char c) { if (c == ',') return ' '; return c; }
26 };
27
28 // This functor translates x to log2(x).
29 struct Log2 {
operator ()Log230 double operator()(input_type::value_type x) { return (x == 0) ? 1e-12 : log2(x); }
31 };
32
33 // This functor returns every time one more.
34 struct PlusOne {
35 int offset;
PlusOnePlusOne36 PlusOne(int start) : offset(start) { }
operator ()PlusOne37 int operator()(void) { return offset++; }
38 };
39
40 // A line y = a * x + b.
41 struct Line {
42 double a;
43 double b;
44 };
45
46 // Fit's a line through the set of points (x, y) by minimizing \sum_{i} { (y_i - (a x_i + b))^2 w_i }.
47 template<typename InItX, typename InItY, typename InItW>
fit_line(InItX x_begin,InItX x_end,InItY y_begin,InItW w_begin)48 Line fit_line(InItX x_begin, InItX x_end, InItY y_begin, InItW w_begin)
49 {
50 double w_sum = 0, xw_sum = 0, yw_sum = 0;
51 for (InItX x = x_begin, y = y_begin, w = w_begin; x != x_end; ++x, ++y, ++w)
52 {
53 w_sum += *w;
54 xw_sum += *x * *w;
55 yw_sum += *y * *w;
56 }
57 double x_mean = xw_sum / w_sum;
58 double y_mean = yw_sum / w_sum;
59 double n1 = 0, n2 = 0;
60 for (InItX x = x_begin, y = y_begin, w = w_begin; x != x_end; ++x, ++y, ++w)
61 {
62 n1 += *x * *w * (*y - y_mean);
63 n2 += *x * *w * (*x - x_mean);
64 }
65 Line result;
66 result.a = n1 / n2;
67 double b_sum = 0;
68 for (InItX x = x_begin, y = y_begin, w = w_begin; x != x_end; ++x, ++y, ++w)
69 b_sum += (*y - result.a * *x) * *w;
70 result.b = b_sum / w_sum;
71 return result;
72 }
73
74 // Pretty print a matrix object.
75 template<typename T>
print(boost::numeric::ublas::matrix<T> const & m)76 void print(boost::numeric::ublas::matrix<T> const& m)
77 {
78 cout << '\n';
79 size_t width = 0;
80 for (int row = 0; row < m.size1(); ++row)
81 {
82 for (int col = 0; col < m.size2(); ++col)
83 {
84 std::stringstream tmp;
85 tmp << m(row, col);
86 width = std::max(width, tmp.str().size());
87 }
88 }
89 width += 2;
90 for (int row = 0; row < m.size1(); ++row)
91 {
92 for (int col = 0; col < m.size2(); ++col)
93 cout << std::setw(width) << m(row, col);
94 cout << '\n';
95 }
96 }
97
98 // This function returns the number of non-zero rows after 'wiping' the matrix 'm',
99 // or zero when the wipe was successful (no non-zero rows occurred).
100 // The matrix must be n x (n+1). The extra column is in fact the result u of
101 // the matrix equation M v = u. This function then solves v by wiping the lower-left
102 // triangle of the square nxn matrix M, while updating u in order to keep the
103 // equation valid.
104 template<typename T>
wipe(boost::numeric::ublas::matrix<T> & m)105 int wipe(boost::numeric::ublas::matrix<T>& m)
106 {
107 // Run over the diagonal elements (k, k) of the matrix.
108 for (unsigned int k = 0; k < m.size1(); ++k)
109 {
110 T f = m(k, k);
111 for (unsigned int row = k + 1; row < m.size1(); ++row)
112 {
113 bool nonzero = false;
114 for (unsigned int col = k + 1; col < m.size2(); ++col)
115 if (((m(row, col) *= f) -= m(row, k) * m(k, col)) != 0)
116 nonzero = true;
117 m(row, k) = 0;
118 if (!nonzero)
119 return k + 1;
120 }
121 }
122 return 0;
123 }
124
125 // Return true if this finds a recursive linear dependence in the sequence 'a'.
126 // a(n_begin), a(n_begin+1), ..., a(n_end-1) must be given.
127 // If the function returns true, 'solution' contains first a constant offset,
128 // followed by coefficients relative to a(n-i). Thus, a(n) = solution[0] + \sum_{i}{solution[i] * a(n-i)}.
solve(unsigned long * a,int n_begin,int n_end,std::vector<long> & solution)129 bool solve(unsigned long* a, int n_begin, int n_end, std::vector<long>& solution)
130 {
131 // We start with trying the maximum number of variables.
132 unsigned int rows = (n_end - n_begin + 1) / 2;
133 while (rows)
134 {
135 // Declare and fill the matrix.
136 boost::numeric::ublas::matrix<mpz_class> m(rows, rows + 1);
137 for (unsigned int row = 0; row < rows; ++row)
138 {
139 m(row, 0) = 1;
140 for (unsigned int col = 1; col < rows; ++col)
141 m(row, col) = a[n_begin + row + rows - 1 - col];
142 m(row, rows) = a[n_begin + row + rows - 1];
143 }
144 // Solve the problem halfway. This returns 0 if it works,
145 // otherwise the correct number of rows is returned and we try once more.
146 if (!(rows = wipe(m)))
147 {
148 rows = m.size1();
149 for (int row = rows - 1; row >= 0; --row)
150 {
151 // We should have exactly one non-zero value in the first 'rows' columns that devides the last column.
152 mpz_class x = 0, y;
153 int c;
154 for (unsigned int col = 0; col < rows; ++col)
155 {
156 y = m(row, col);
157 if (y != 0)
158 {
159 if (x != 0)
160 return false; // Failure.
161 x = y;
162 c = col;
163 }
164 }
165 if (x == 0 || m(row, rows) % x != 0)
166 return false; // Failure.
167 m(row, rows) /= x;
168 m(row, c) = 1;
169 for (int row2 = row - 1; row2 >= 0; --row2)
170 {
171 m(row2, rows) -= m(row, rows) * m(row2, c);
172 m(row2, c) = 0;
173 }
174 }
175 // We found a solution!
176 for (unsigned int row = 0; row < rows; ++row)
177 {
178 assert(m(row, rows).fits_slong_p());
179 solution.push_back(m(row, rows).get_si());
180 }
181 // Or did we?
182 for (int n = n_begin + solution.size() - 1; n < n_end; ++n)
183 {
184 unsigned long an = solution[0];
185 for (unsigned int row = 1; row < rows; ++row)
186 an += solution[row] * a[n - row];
187 if (an != a[n])
188 return false;
189 }
190 break;
191 }
192 }
193 return true;
194 }
195
196 // The format used in the table 'brute_force_sequences'.
197 struct BruteForceSequences {
198 char code;
199 char const* description;
200 unsigned long terms[31];
201 };
202
203 // Default sequences.
204 BruteForceSequences brute_force_sequences[] = {
205 { 'A', "Number of Frobenius equivalence classes (foc(m) / m = A001037)", { 2, 1, 2, 3, 6, 9, 18, 30, 56, 99, 186, 335, 630, 1161, 2182, 4080, 7710, 14532, 27594, 52377, 99858, 190557, 364722, 698870, 1342176, 2580795, 4971008, 9586395, 18512790, 35790267, 69273666 } },
206 { 'B', "Number of Frobenius equivalence classes with trace 1 (blwt1(m) = A000048 (see also proposition 3))", { 1, 1, 1, 2, 3, 5, 9, 16, 28, 51, 93, 170, 315, 585, 1091, 2048, 3855, 7280, 13797, 26214, 49929, 95325, 182361, 349520, 671088, 1290555, 2485504, 4793490, 9256395, 17895679, 34636833 } },
207 { 'C', "Number of Frobenius equivalence classes that are their own inverse (blwt1(m/2) (propositions 2 and 5))", { 1, 1, 0, 1, 0, 1, 0, 2, 0, 3, 0, 5, 0, 9, 0, 16, 0, 28, 0, 51, 0, 93, 0, 170, 0, 315, 0, 585, 0, 1091, 0 } },
208 { 'D', "Number of Frobenius equivalence classes that are their own inverse with trace 1 (depends on w(m/2, 1)*)", { 1, 1, 0, 1, 0, 0, 0, 1, 0, 2, 0, 2, 0, 4, 0, 9, 0, 14, 0, 24, 0, 48, 0, 86, 0, 154, 0, 294, 0, 550, 0 } },
209 // { 'E', "Number of generators (euler_phi(2^m - 1) / m = A011260)", { 1, 1, 2, 2, 6, 6, 18, 16, 48, 60, 176, 144, 630, 756, 1800, 2048, 7710, 7776, 27594, 24000, 84672, 120032, 356960, 276480, 1296000, 1719900, 4202496, 4741632, 18407808, 17820000, 69273666 } },
210 // { 'F', "Number of generators with trace 1 (no formula)", { 1, 1, 1, 1, 3, 4, 9, 7, 25, 31, 87, 72, 315, 381, 901, 1017, 3855, 3890, 13797, 12000, 42344, 60043, 178431, 138224, 648031, 860059, 2101353, 2370715, 9203747, 8908940, 34636833 } },
211 // { 'G', "Number of normal basis (num_normal(m) = A027362)", { 1, 1, 1, 2, 3, 4, 7, 16, 21, 48, 93, 128, 315, 448, 675, 2048, 3825, 5376, 13797, 24576, 27783, 95232, 182183, 262144, 629145, 1290240, 1835001, 3670016, 9256395, 11059200, 28629151 } },
212 // { 'H', "Number of normal basis that exist of generators (no formula, A107222)", { 1, 1, 1, 1, 3, 3, 7, 7, 19, 29, 87, 52, 315, 291, 562, 1017, 3825, 2870, 13797, 11255, 23579, 59986, 178259, 103680, 607522, 859849, 1551227, 1815045, 9203747, 5505966, 28629151 } },
213 { 'I', "Number of solutions, w(m, 1) (this is what we want to know) (A137503)", { 1, 1, 0, 1, 4, 3, 8, 16, 28, 45, 96, 167, 308, 579, 1100, 2018, 3852, 7280, 13776, 26133, 49996, 95223, 182248, 349474, 671176, 1289925, 2485644, 4793355, 9255700, 17894421, 34638296 } },
214 { 'J', "Number of solutions with trace 1 (no formula)", { 1, 1, 0, 1, 2, 2, 4, 9, 14, 24, 48, 86, 154, 294, 550, 1017, 1926, 3654, 6888, 13092, 24998, 47658, 91124, 174822, 335588, 645120, 1242822, 2396970, 4627850, 8947756, 17319148 } },
215 // { 'K', "Number of solutions that are generators (no formula)", { 1, 1, 0, 0, 4, 2, 8, 10, 26, 26, 94, 76, 308, 378, 902, 1014, 3852, 3908, 13776, 11944, 42460, 60014, 178414, 138360, 648042, 859642, 2101290, 2370858, 9202954, 8910288, 34638296 } },
216 // { 'L', "Number of solutions that are generators with trace 1 (no formula)", { 1, 1, 0, 0, 2, 2, 4, 4, 14, 14, 46, 38, 154, 192, 452, 500, 1926, 1956, 6888, 5972, 21238, 30034, 89158, 69164, 324052, 429930, 1050750, 1185328, 4601320, 4454084, 17319148 } },
217 // { 'M', "Number of solutions that are normal basis (no formula)", { 1, 1, 0, 1, 2, 2, 4, 9, 12, 22, 48, 62, 154, 224, 332, 1017, 1912, 2712, 6888, 12286, 13912, 47610, 91028, 131094, 314584, 644966, 917600, 1835144, 4627850, 5529896, 14314976 } },
218 // { 'N', "Number of solutions that are normal basis and generators (no formula)", { 1, 1, 0, 0, 2, 2, 4, 4, 12, 13, 46, 25, 154, 145, 277, 500, 1912, 1459, 6888, 5603, 11876, 30005, 89062, 51888, 303761, 429829, 775566, 907684, 4601320, 2753361, 14314976 } }
219 };
220 unsigned int const number_of_brute_force_sequences = sizeof(brute_force_sequences) / sizeof(BruteForceSequences);
221
222 enum split_nt {
223 not_split,
224 odd_n,
225 even_n
226 };
227
228 class Filter {
229 public:
230 virtual unsigned int loop_end(void) = 0; // Returns one beyond the maximum loop counter value.
231
232 virtual bool split_apply(unsigned long* terms, int n_begin, int n_end, int loop_counter, split_nt& split); // Returns true if successful.
233 virtual void split_print_formula_on(std::ostream& os, char sn, int loop_counter, split_nt split);
234 virtual void split_print_inverse_formula_on(std::ostream& os, char sn, int loop_counter, split_nt split);
235
236 virtual bool apply(unsigned long* terms, int n_begin, int n_end, int loop_counter) = 0;
237 virtual void print_formula_on(std::ostream& os, char sn, int loop_counter) = 0;
238 virtual void print_inverse_formula_on(std::ostream& os, char sn, int loop_counter) = 0;
239 };
240
split_apply(unsigned long * terms,int n_begin,int n_end,int loop_counter,split_nt & split)241 bool Filter::split_apply(unsigned long* terms, int n_begin, int n_end, int loop_counter, split_nt& split)
242 {
243 if (split == not_split)
244 return apply(terms, n_begin, n_end, loop_counter);
245 bool success;
246 if ((n_begin & 1) == 0) // n_begin is even?
247 {
248 unsigned int number_of_even_terms = (n_end - n_begin + 1) / 2;
249 unsigned int number_of_odd_terms = (n_end - n_begin) / 2;
250 n_begin /= 2;
251 success = apply(terms, n_begin, n_begin + number_of_even_terms, loop_counter);
252 terms += n_begin + number_of_even_terms;
253 success = apply(terms, n_begin, n_begin + number_of_odd_terms, loop_counter) && success;
254 }
255 else // n_begin is odd.
256 {
257 unsigned int number_of_even_terms = (n_end - n_begin) / 2;
258 unsigned int number_of_odd_terms = (n_end - n_begin + 1) / 2;
259 n_begin = (n_begin - 1) / 2;
260 if (n_begin == 0)
261 {
262 n_begin = 1;
263 --number_of_odd_terms;
264 }
265 success = apply(terms, n_begin, n_begin + number_of_odd_terms, loop_counter);
266 terms += n_begin + number_of_odd_terms;
267 success = apply(terms, n_begin, n_begin + number_of_even_terms, loop_counter) && success;
268 }
269 return success;
270 }
271
split_print_formula_on(std::ostream & os,char sn,int loop_counter,split_nt split)272 void Filter::split_print_formula_on(std::ostream& os, char sn, int loop_counter, split_nt split)
273 {
274 if (split == even_n)
275 print_formula_on(os, sn + 13, loop_counter);
276 else
277 print_formula_on(os, sn, loop_counter);
278 }
279
split_print_inverse_formula_on(std::ostream & os,char sn,int loop_counter,split_nt split)280 void Filter::split_print_inverse_formula_on(std::ostream& os, char sn, int loop_counter, split_nt split)
281 {
282 if (split == even_n)
283 print_inverse_formula_on(os, sn + 13, loop_counter);
284 else
285 print_inverse_formula_on(os, sn, loop_counter);
286 }
287
288 class Split : public Filter {
289 public:
loop_end(void)290 virtual unsigned int loop_end(void) { return 2; }
291 virtual bool split_apply(unsigned long* terms, int n_begin, int n_end, int loop_counter, split_nt& split);
292 virtual void split_print_formula_on(std::ostream& os, char sn, int loop_counter, split_nt split);
293 virtual void split_print_inverse_formula_on(std::ostream& os, char sn, int loop_counter, split_nt split);
apply(unsigned long *,int,int,int)294 virtual bool apply(unsigned long*, int, int, int) { return false; /* dummy */ }
print_formula_on(std::ostream &,char,int)295 virtual void print_formula_on(std::ostream&, char, int) { /* dummy */ }
print_inverse_formula_on(std::ostream &,char,int)296 virtual void print_inverse_formula_on(std::ostream&, char, int) { /* dummy */ }
297 };
298
299 class multiply_with_n : public Filter {
300 public:
loop_end(void)301 virtual unsigned int loop_end(void) { return 2; }
302 virtual bool apply(unsigned long* terms, int n_begin, int n_end, int loop_counter); // Returns true if successful.
303 virtual void print_formula_on(std::ostream& os, char sn, int loop_counter);
304 virtual void print_inverse_formula_on(std::ostream& os, char sn, int loop_counter);
305 };
306
307 class add_divisors : public Filter {
308 private:
309 bool no_n;
310 public:
loop_end(void)311 virtual unsigned int loop_end(void) { return 4; }
312 virtual bool apply(unsigned long* terms, int n_begin, int n_end, int loop_counter); // Returns true if successful.
313 virtual void print_formula_on(std::ostream& os, char sn, int loop_counter);
314 virtual void print_inverse_formula_on(std::ostream& os, char sn, int loop_counter);
315 };
316
317 class phi_inv : public Filter {
318 private:
319 unsigned int M_loop_end;
320 public:
phi_inv(void)321 phi_inv(void) : M_loop_end(2) { } // The real value is the first time apply is called (with loop_counter == 1).
loop_end(void)322 virtual unsigned int loop_end(void) { return M_loop_end; }
323 virtual bool apply(unsigned long* terms, int n_begin, int n_end, int loop_counter); // Returns true if successful.
324 virtual void print_formula_on(std::ostream& os, char sn, int loop_counter);
325 virtual void print_inverse_formula_on(std::ostream& os, char sn, int loop_counter);
326 };
327
328 std::vector<Filter*> filters;
329
main(int argc,char * argv[])330 int main(int argc, char* argv[])
331 {
332 program_name = argv[0];
333
334 initialize_utils();
335
336 // Default values for the table.
337 int n_begin = 1;
338 int number_of_terms = 31;
339 bool use_table = true;
340
341 // Vector used to buffer a possible commandline sequence.
342 input_type input;
343
344 // There must be at least two terms.
345 if (argc == 2)
346 {
347 cout << "argc = " << argc << endl;
348 cout << "Usage: " << program_name << " <n_begin> <term>,<term>,<term>,<term>[,<term>...]" << endl;
349 return 1;
350 }
351 else if (argc > 1)
352 {
353 use_table = false;
354
355 // The first argument must be the index of the first term.
356 n_begin = atoi(argv[1]);
357
358 // Catenate the rest.
359 std::string sequence_str;
360 for (int i = 2; i < argc; ++i)
361 {
362 sequence_str += ' ';
363 sequence_str += argv[i];
364 }
365 // Replace all comma's with spaces.
366 std::transform(sequence_str.begin(), sequence_str.end(), sequence_str.begin(), CommaToSpace());
367 // Read the sequence into a vector.
368 std::stringstream buf;
369 buf << sequence_str;
370 input_type::value_type term;
371 while (buf >> term)
372 input.push_back(term);
373 number_of_terms = input.size();
374
375 cout << "Input: ";
376 unsigned int n = n_begin;
377 for (input_type::const_iterator iter = input.begin(); iter != input.end(); ++iter, ++n)
378 cout << "a(" << n << ") = " << *iter << "; ";
379 cout << '\n';
380 }
381
382 filters.push_back(new multiply_with_n);
383 filters.push_back(new add_divisors);
384 filters.push_back(new Split);
385 // filters.push_back(new phi_inv);
386
387 unsigned int const number_of_filters = filters.size();
388
389 // If there is a sequence given on the command line try that, otherwise try every sequence given in the table.
390 for (unsigned int sequence = 0; sequence < (use_table ? number_of_brute_force_sequences : 1); ++sequence)
391 {
392 if (use_table)
393 {
394 // Print a little header before each sequence from the table.
395 if (sequence != 0)
396 cout << endl;
397 cout << brute_force_sequences[sequence].code << ": " << brute_force_sequences[sequence].description << ":\n";
398 }
399
400 // Make a copy.
401 unsigned long terms[n_begin + number_of_terms];
402 for (int n = n_begin; n < n_begin + number_of_terms; ++n)
403 terms[n] = use_table ? brute_force_sequences[sequence].terms[n - n_begin] : input[n - n_begin];
404 split_nt split = not_split;
405
406 char sn = 'a';
407 for (MultiLoop ml(number_of_filters); !ml.finished(); ml.next_loop())
408 {
409 for(; ml() < filters[*ml]->loop_end(); ++ml)
410 {
411 if (*ml == number_of_filters - 1) // Inner most loop.
412 {
413 // Make a list of all filters that are currently turned on.
414 std::vector<Filter*> current_filters;
415 std::vector<int> current_loop_counters;
416 int filter_index = 0;
417 for (std::vector<Filter*>::iterator filter = filters.begin(); filter != filters.end(); ++filter, ++filter_index)
418 if (ml[filter_index] != 0)
419 {
420 current_filters.push_back(*filter);
421 current_loop_counters.push_back(ml[filter_index]);
422 }
423 // Generate the first permutation of the order in which to apply these filters.
424 std::vector<int> permutation(current_filters.size());
425 for (unsigned int i = 0; i < permutation.size(); ++i)
426 permutation[i] = i;
427 // Run over all permutations.
428 do
429 {
430 if (sn != 'a') // Applied filters before?
431 {
432 // Put back the original sequence.
433 for (int n = n_begin; n < n_begin + number_of_terms; ++n)
434 terms[n] = use_table ? brute_force_sequences[sequence].terms[n - n_begin] : input[n - n_begin];
435 split = not_split;
436
437 // Apply the current filters in the order given by permutation.
438 bool success = true;
439 sn = 'a';
440 for (std::vector<int>::const_iterator perm = permutation.begin(); perm != permutation.end(); ++perm)
441 {
442 ++sn;
443 if (!current_filters[*perm]->split_apply(terms, n_begin, n_begin + number_of_terms, current_loop_counters[*perm], split))
444 {
445 success = false;
446 break;
447 }
448 cout << "Let ";
449 current_filters[*perm]->split_print_formula_on(cout, sn, current_loop_counters[*perm], split);
450 cout << '\n';
451 }
452 if (!success)
453 continue; // Try next permutation.
454 }
455
456 unsigned long* split_terms = terms;
457 int split_n_begin = (split == not_split) ? n_begin : (n_begin / 2);
458 unsigned int split_number_of_terms = (split == not_split) ? number_of_terms : ((number_of_terms + 1) / 2); // The first batch of terms.
459 unsigned int split_number_of_terms_next = number_of_terms / 2; // The second batch of terms.
460
461 for (int batch = 0; batch < 2; ++batch)
462 {
463 if (batch == 1)
464 {
465 split_n_begin = (n_begin + 1) / 2;
466 split_terms += split_number_of_terms - ((split == even_n) ? 0 : 1);
467 split_number_of_terms = split_number_of_terms_next;
468 split = (((n_begin + 1) & 1) ? odd_n : even_n);
469 }
470
471 int split_n_end = split_n_begin + split_number_of_terms;
472 char split_sn = (split == even_n) ? (sn + 13) : sn;
473
474 // Print out the sequence that we're going to try and find a linear dependency for.
475 cout << "Trying to solve " << split_sn << "(n = " << split_n_begin << "...) = ";
476 for (int n = split_n_begin; n < split_n_end; ++n)
477 cout << split_terms[n] << ", ";
478 cout << "...\n";
479
480 #if 0
481 // Guess if this is a sequence of elements (2^(m - c0) + epsilon), or of FEC (2^(m - c0) / m + epsilon).
482 // We do this by taking the logarithm.
483 std::vector<double> input_log2(split_n_end - split_n_begin);
484 std::transform(split_terms + split_n_begin, split_terms + split_n_end, input_log2.begin(), Log2());
485 // The 'x-axis' values.
486 std::vector<double> x(split_n_end - split_n_begin);
487 std::generate(x.begin(), x.end(), PlusOne(split_n_begin));
488 // Then fit a straight line through this, weighting the points with the original terms (larger values are more important).
489 std::vector<double> weight;
490 for (int n = split_n_begin; n < split_n_end; ++n)
491 {
492 if (split_terms[n] <= 2)
493 weight.push_back(0);
494 else
495 {
496 double delta = log2((split_terms[n] + 1.0 + sqrt(split_terms[n])) / (split_terms[n] - 1.0 - sqrt(split_terms[n])));
497 weight.push_back(1.0 / (delta * delta));
498 }
499 }
500 // Fit a weighted line.
501 Line line = fit_line(x.begin(), x.end(), input_log2.begin(), weight.begin());
502 cout << "Line fit: " << sn << "(n) = " << exp2(line.a) << "^n * " << exp2(line.b) << endl;
503 #endif
504
505 std::vector<long> solution;
506 if (solve(split_terms, split_n_begin, split_n_end, solution))
507 {
508 if (split == not_split)
509 cout << "* Found!\n* ";
510 else if (split == odd_n)
511 cout << "* Found for odd n!\n* ";
512 else
513 cout << "* Found for even n!\n* ";
514 for (unsigned int n = split_n_begin; n < split_n_begin + solution.size() - 1; ++n)
515 cout << split_sn << "(" << n << ") = " << split_terms[n] << "; ";
516 cout << split_sn << "(n) = ";
517 for (unsigned int i = 0; i < solution.size(); ++i)
518 {
519 if (i == 0)
520 cout << solution[0];
521 else
522 {
523 long f = solution[i];
524 if (f == 0)
525 continue;
526 if (f < 0)
527 {
528 cout << " - ";
529 f = -f;
530 }
531 else
532 cout << " + ";
533 if (f > 1)
534 cout << f << ' ';
535 cout << split_sn << "(n - " << i << ')';
536 }
537 }
538 cout << endl;
539 for (std::vector<int>::const_reverse_iterator perm = permutation.rbegin(); perm != permutation.rend(); ++perm)
540 {
541 cout << "* ";
542 current_filters[*perm]->split_print_inverse_formula_on(cout, sn, current_loop_counters[*perm], split);
543 --split_sn;
544 cout << '\n';
545 }
546 //goto solved;
547 }
548 if (split == not_split)
549 break;
550 }
551 sn = 'b'; // Make sure we apply filters the next loop.
552 }
553 while (std::next_permutation(permutation.begin(), permutation.end()));
554 }
555 }
556 }
557 //solved:
558 while(0);
559 // Next sequence.
560 }
561 }
562
split_print_formula_on(std::ostream & os,char b,int,split_nt split)563 void Split::split_print_formula_on(std::ostream& os, char b, int, split_nt split)
564 {
565 char a = b - 1;
566 if (split == even_n)
567 b += 13;
568 if (split == odd_n)
569 os << b << "(n) = " << a << "(2n + 1)";
570 else
571 os << b << "(n) = " << a << "(2n)";
572 }
573
split_print_inverse_formula_on(std::ostream & os,char b,int,split_nt split)574 void Split::split_print_inverse_formula_on(std::ostream& os, char b, int, split_nt split)
575 {
576 char a = b - 1;
577 if (split == even_n)
578 b += 13;
579 if (split == odd_n)
580 os << a << "(n) = " << b << "((n - 1) / 2), n odd";
581 else
582 os << a << "(n) = " << b << "(n / 2), n even";
583 }
584
split_apply(unsigned long * terms,int n_begin,int n_end,int,split_nt & split)585 bool Split::split_apply(unsigned long* terms, int n_begin, int n_end, int, split_nt& split)
586 {
587 int first_n_begin = n_begin / 2;
588 int first_number_of_terms = (n_end - n_begin + 1) / 2;
589 int second_n_begin = first_n_begin + first_number_of_terms;
590 int second_number_of_terms = (n_end - n_begin) / 2;
591 unsigned long second_entries[second_number_of_terms];
592 for (int i = 0; i < second_number_of_terms; ++i)
593 second_entries[i] = terms[n_begin + 1 + 2 * i];
594 for (int i = 0; i < first_number_of_terms; ++i)
595 terms[first_n_begin + i] = terms[n_begin + 2 * i];
596 for (int i = 0; i < second_number_of_terms; ++i)
597 terms[second_n_begin + i] = second_entries[i];
598 split = (n_begin & 1) ? odd_n : even_n;
599 return true;
600 }
601
print_formula_on(std::ostream & os,char b,int)602 void multiply_with_n::print_formula_on(std::ostream& os, char b, int)
603 {
604 char a = b - 1;
605 os << b << "(n) = n * " << a << "(n)";
606 }
607
print_inverse_formula_on(std::ostream & os,char b,int)608 void multiply_with_n::print_inverse_formula_on(std::ostream& os, char b, int)
609 {
610 char a = b - 1;
611 os << a << "(n) = " << b << "(n) / n";
612 }
613
apply(unsigned long * terms,int n_begin,int n_end,int)614 bool multiply_with_n::apply(unsigned long* terms, int n_begin, int n_end, int)
615 {
616 for (int n = n_begin; n < n_end; ++n)
617 terms[n] *= n;
618 return true;
619 }
620
print_formula_on(std::ostream & os,char b,int loop_counter)621 void add_divisors::print_formula_on(std::ostream& os, char b, int loop_counter)
622 {
623 char a = b - 1;
624 os << b << "(n) = " << a << "(n) + \\sum_{";
625 if (no_n)
626 os << "1<d<n";
627 else
628 os << "1<d<=n";
629 if (loop_counter == 1)
630 os << ",odd d";
631 if (loop_counter == 2)
632 os << ",even d";
633 os << ",d|n} { " << a << "(n/d) }";
634 }
635
print_inverse_formula_on(std::ostream & os,char b,int loop_counter)636 void add_divisors::print_inverse_formula_on(std::ostream& os, char b, int loop_counter)
637 {
638 char a = b - 1;
639 os << a << "(n) = " << b << "(n) - \\sum_{";
640 if (no_n)
641 os << "1<d<n";
642 else
643 os << "1<d<=n";
644 if (loop_counter == 1)
645 os << ",odd d";
646 if (loop_counter == 2)
647 os << ",even d";
648 os << ",d|n} { " << a << "(n/d) }";
649 }
650
apply(unsigned long * terms,int n_begin,int n_end,int loop_counter)651 bool add_divisors::apply(unsigned long* terms, int n_begin, int n_end, int loop_counter)
652 {
653 // loop_counter
654 // 1: odd divisors.
655 // 2: even divisors.
656 // 3: both.
657 if (n_begin > 2)
658 return false;
659 no_n = (n_begin == 1);
660 for (int n = n_end - 1; n >= n_begin; --n)
661 {
662 for (int d = 2; d <= (no_n ? n - 1 : n); ++d)
663 {
664 int eo = 2 - (d & 1);
665 if ((eo & loop_counter) && n % d == 0)
666 terms[n] += terms[n/d];
667 }
668 }
669 return true;
670 }
671
print_formula_on(std::ostream & os,char b,int loop_counter)672 void phi_inv::print_formula_on(std::ostream& os, char b, int loop_counter)
673 {
674 char a = b - 1;
675 os << b << "(n) = euler_phi_inv(" << a << "(n)) #" << loop_counter;
676 }
677
print_inverse_formula_on(std::ostream & os,char b,int)678 void phi_inv::print_inverse_formula_on(std::ostream& os, char b, int)
679 {
680 char a = b - 1;
681 os << a << "(n) = euler_phi(" << b << "(n))";
682 }
683
apply(unsigned long * terms,int n_begin,int n_end,int loop_counter)684 bool phi_inv::apply(unsigned long* terms, int n_begin, int n_end, int loop_counter)
685 {
686 for (int n = n_begin; n < n_end; ++n)
687 {
688 unsigned long term = terms[n];
689 if ((term & 1) == 1 && term != 1)
690 return false;
691 }
692 if (loop_counter == 1)
693 {
694 M_loop_end = 1;
695 for (int n = n_begin; n < n_end; ++n)
696 {
697 std::vector<unsigned long> result;
698 cout << "Calculating euler_phi_inv(" << terms[n] << ");";
699 euler_phi_inv(terms[n], result);
700 cout << " number of solutions: " << result.size() << ": ";
701 unsigned int count = 1;
702 for (std::vector<unsigned long>::iterator iter = result.begin(); iter != result.end(); ++iter, ++count)
703 {
704 cout << *iter << ", ";
705 if (count == 9)
706 {
707 cout << "...";
708 break;
709 }
710 }
711 cout << '\n';
712 M_loop_end *= result.size();
713 if (M_loop_end == 0)
714 {
715 M_loop_end = 2;
716 return false;
717 }
718 }
719 ++M_loop_end;
720 }
721 else if (M_loop_end == 0)
722 return false;
723 cout << "WE GET HERE with M_loop_end = " << M_loop_end << endl;
724 return true;
725 }
726