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