import std.algorithm; import std.array; import std.conv; import std.datetime.stopwatch; import std.math; import std.stdio; struct Term { ulong coeff; byte ix1, ix2; } enum maxDigits = 16; ulong toUlong(byte[] digits, bool reverse) { ulong sum = 0; if (reverse) { for (int i = digits.length - 1; i >= 0; --i) { sum = sum * 10 + digits[i]; } } else { for (size_t i = 0; i < digits.length; ++i) { sum = sum * 10 + digits[i]; } } return sum; } bool isSquare(ulong n) { if ((0x202021202030213 & (1 << (n & 63))) != 0) { auto root = cast(ulong)sqrt(cast(double)n); return root * root == n; } return false; } byte[] seq(byte from, byte to, byte step) { byte[] res; for (auto i = from; i <= to; i += step) { res ~= i; } return res; } string commatize(ulong n) { auto s = n.to!string; auto le = s.length; for (int i = le - 3; i >= 1; i -= 3) { s = s[0..i] ~ "," ~ s[i..$]; } return s; } void main() { auto sw = StopWatch(AutoStart.yes); ulong pow = 1; writeln("Aggregate timings to process all numbers up to:"); // terms of (n-r) expression for number of digits from 2 to maxDigits Term[][] allTerms = uninitializedArray!(Term[][])(maxDigits - 1); for (auto r = 2; r <= maxDigits; r++) { Term[] terms; pow *= 10; ulong pow1 = pow; ulong pow2 = 1; byte i1 = 0; byte i2 = cast(byte)(r - 1); while (i1 < i2) { terms ~= Term(pow1 - pow2, i1, i2); pow1 /= 10; pow2 *= 10; i1++; i2--; } allTerms[r - 2] = terms; } // map of first minus last digits for 'n' to pairs giving this value byte[][][byte] fml = [ 0: [[2, 2], [8, 8]], 1: [[6, 5], [8, 7]], 4: [[4, 0]], 6: [[6, 0], [8, 2]] ]; // map of other digit differences for 'n' to pairs giving this value byte[][][byte] dmd; for (byte i = 0; i < 100; i++) { byte[] a = [i / 10, i % 10]; auto d = a[0] - a[1]; dmd[cast(byte)d] ~= a; } byte[] fl = [0, 1, 4, 6]; auto dl = seq(-9, 9, 1); // all differences byte[] zl = [0]; // zero diferences only auto el = seq(-8, 8, 2); // even differences only auto ol = seq(-9, 9, 2); // odd differences only auto il = seq(0, 9, 1); ulong[] rares; byte[][][] lists = uninitializedArray!(byte[][][])(4); foreach (i, f; fl) { lists[i] = [[f]]; } byte[] digits; int count = 0; // Recursive closure to generate (n+r) candidates from (n-r) candidates // and hence find Rare numbers with a given number of digits. void fnpr(byte[] cand, byte[] di, byte[][] dis, byte[][] indicies, ulong nmr, int nd, int level) { if (level == dis.length) { digits[indicies[0][0]] = fml[cand[0]][di[0]][0]; digits[indicies[0][1]] = fml[cand[0]][di[0]][1]; auto le = di.length; if (nd % 2 == 1) { le--; digits[nd / 2] = di[le]; } foreach (i, d; di[1..le]) { digits[indicies[i + 1][0]] = dmd[cand[i + 1]][d][0]; digits[indicies[i + 1][1]] = dmd[cand[i + 1]][d][1]; } auto r = toUlong(digits, true); auto npr = nmr + 2 * r; if (!isSquare(npr)) { return; } count++; writef(" R/N %2d:", count); auto ms = sw.peek(); writef(" %9s", ms); auto n = toUlong(digits, false); writef(" (%s)\n", commatize(n)); rares ~= n; } else { foreach (num; dis[level]) { di[level] = num; fnpr(cand, di, dis, indicies, nmr, nd, level + 1); } } } // Recursive closure to generate (n-r) candidates with a given number of digits. void fnmr(byte[] cand, byte[][] list, byte[][] indicies, int nd, int level) { if (level == list.length) { ulong nmr, nmr2; foreach (i, t; allTerms[nd - 2]) { if (cand[i] >= 0) { nmr += t.coeff * cand[i]; } else { nmr2 += t.coeff * -cast(int)(cand[i]); if (nmr >= nmr2) { nmr -= nmr2; nmr2 = 0; } else { nmr2 -= nmr; nmr = 0; } } } if (nmr2 >= nmr) { return; } nmr -= nmr2; if (!isSquare(nmr)) { return; } byte[][] dis; dis ~= seq(0, cast(byte)(fml[cand[0]].length - 1), 1); for (auto i = 1; i < cand.length; i++) { dis ~= seq(0, cast(byte)(dmd[cand[i]].length - 1), 1); } if (nd % 2 == 1) { dis ~= il; } byte[] di = uninitializedArray!(byte[])(dis.length); fnpr(cand, di, dis, indicies, nmr, nd, 0); } else { foreach (num; list[level]) { cand[level] = num; fnmr(cand, list, indicies, nd, level + 1); } } } for (int nd = 2; nd <= maxDigits; nd++) { digits = uninitializedArray!(byte[])(nd); if (nd == 4) { lists[0] ~= zl; lists[1] ~= ol; lists[2] ~= el; lists[3] ~= ol; } else if (allTerms[nd - 2].length > lists[0].length) { for (int i = 0; i < 4; i++) { lists[i] ~= dl; } } byte[][] indicies; foreach (t; allTerms[nd - 2]) { indicies ~= [t.ix1, t.ix2]; } foreach (list; lists) { byte[] cand = uninitializedArray!(byte[])(list.length); fnmr(cand, list, indicies, nd, 0); } auto ms = sw.peek(); writefln(" %2d digits: %9s", nd, ms); } rares.sort; writefln("\nThe rare numbers with up to %d digits are:", maxDigits); foreach (i, rare; rares) { writefln(" %2d: %25s", i + 1, commatize(rare)); } }