# Intro

Consider the process of taking some positive integer n in some base b and replacing each digit with its representation in the base of the digit to the right.

• If the digit to the right is a 0, use base b.
• If the digit to the right is a 1, use unary with 0's as tally marks.
• If there is no digit to the right (i.e. you are in the ones place), loop around to the most significant digit.

As an example let n = 160 and b = 10. Running the process looks like this:

The first digit is 1, the digit to the right is 6, 1 in base 6 is 1.
The next digit is 6, the digit to the right is 0, 0 is not a base so use b, 6 in base b is 6.
The last digit is 0, the digit to the right (looping around) is 1, 0 in base 1 is the empty string (but that's ok).

Concatenating '1', '6', and '' together gives 16, which is read in the original base b = 10.


The exact same procedure but moving left instead of right can also be done:

The first digit is 1, the digit to the left (looping around) is 0, 0 is not a base so use b, 1 in base b is 1.
The next digit is 6, the digit to the left is 1, 6 in base 1 is 000000.
The last digit is 0, the digit to the left is 6, 0 in base 6 is 0.

Concatenating '1', '000000', and '0' together gives 10000000, which is read in the original base b = 10.


Thus, we've made two numbers related to 160 (for b = 10): 16 and 10000000.

We will define n to be a crafty number if it evenly divides at least one of the two numbers generated in this process into 2 or more parts

In the example n is crafty because 160 divides 10000000 exactly 62500 times.

203 is NOT crafty because the resulting numbers are 2011 and 203 itself, which 203 cannot fit evenly into 2 or more times.

# Challenge

(For the rest of the problem we will only consider b = 10.)

The challenge is to write a program that finds the highest crafty number that is also prime.

The first 7 crafty primes (and all that I have found so far) are:

2
5
3449
6287
7589
9397
93557 <-- highest so far (I've searched to 100,000,000+)


I am not officially certain whether more exist, but I expect they do. If you can prove that there are (or aren't) finitely many I'll give you +200 bounty rep.

The winner will be the person who can provide the highest crafty prime, provided it is apparent that they have been active in the search and are not intentionally taking glory from others.

# Rules

• You may use any prime finding tools you want.
• You may use probabilistic prime testers.
• You may reuse other peoples code with attribution. This is a communal effort. Cutthroat tactics will not be tolerated.
• Your program must actively search for the prime. You may start your search at the highest known crafty prime.
• Your program should be able to compute all of the known crafty primes within 4 hours of Amazon EC2 t2.medium instances (either four at once or one for four hours or something in between). I will not actually be testing it on them and you certainly don't need to. This is just a benchmark.

Here is my Python 3 code I used for generating the table above: (runs in a second or two)

import pyprimes

def toBase(base, digit):
a = [
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
['', '0', '00', '000', '0000', '00000', '000000', '0000000', '00000000', '000000000' ],
['0', '1', '10', '11', '100', '101', '110', '111', '1000', '1001'],
['0', '1', '2', '10', '11', '12', '20', '21', '22', '100'],
['0', '1', '2', '3', '10', '11', '12', '13', '20', '21'],
['0', '1', '2', '3', '4', '10', '11', '12', '13', '14'],
['0', '1', '2', '3', '4', '5', '10', '11', '12', '13'],
['0', '1', '2', '3', '4', '5', '6', '10', '11', '12'],
['0', '1', '2', '3', '4', '5', '6', '7', '10', '11'],
['0', '1', '2', '3', '4', '5', '6', '7', '8', '10']
]
return a[base][digit]

def getCrafty(start=1, stop=100000):
for p in pyprimes.primes_above(start):
s = str(p)
left = right = ''
for i in range(len(s)):
digit = int(s[i])
left += toBase(int(s[i - 1]), digit)
right += toBase(int(s[0 if i + 1 == len(s) else i + 1]), digit)
left = int(left)
right = int(right)
if (left % p == 0 and left // p >= 2) or (right % p == 0 and right // p >= 2):
print(p, left, right)
if p >= stop:
break
print('DONE')

getCrafty()

• I think that making 0 in any base x to be the empty string would be more mathematical. Also, I'm sure it would be easier to prove or disprove this version – proud haskeller Aug 11 '14 at 12:12

## Mathematica, finds 93,557 in 0.3s (no further crafty primes below 2*1010)

This is just a naive exhaustive search through all primes. To begin with it checks about 1,000,000 primes every 55 seconds (which is bound to get slower as the primes get larger).

I'm using a bunch of helper functions:

lookup = {
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
{{}, 0, {0, 0}, {0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}},
{0, 1, {1, 0}, {1, 1}, {1, 0, 0}, {1, 0, 1}, {1, 1, 0}, {1, 1, 1}, {1, 0, 0, 0},
{1, 0, 0, 1}},
{0, 1, 2, {1, 0}, {1, 1}, {1, 2}, {2, 0}, {2, 1}, {2, 2}, {1, 0, 0}},
{0, 1, 2, 3, {1, 0}, {1, 1}, {1, 2}, {1, 3}, {2, 0}, {2, 1}},
{0, 1, 2, 3, 4, {1, 0}, {1, 1}, {1, 2}, {1, 3}, {1, 4}},
{0, 1, 2, 3, 4, 5, {1, 0}, {1, 1}, {1, 2}, {1, 3}},
{0, 1, 2, 3, 4, 5, 6, {1, 0}, {1, 1}, {1, 2}},
{0, 1, 2, 3, 4, 5, 6, 7, {1, 0}, {1, 1}},
{0, 1, 2, 3, 4, 5, 6, 7, 8, {1, 0}}
};
convertBase[d_, b_] := lookup[[b + 1, d + 1]];
related[n_] := (
d = IntegerDigits[n];
{FromDigits[Flatten[convertBase @@@ Transpose[{d, RotateRight@d}]]],
FromDigits[Flatten[convertBase @@@ Transpose[{d, RotateLeft@d}]]]}
);
crafty[n_] := (
{ql, qr} = related[n]/n;
IntegerQ[ql] && ql > 1 || IntegerQ[qr] && qr > 1
);


And then this loop does the actual search:

p = 2;
start = TimeUsed[];
i = 1;
While[True,
If[crafty[p], Print@{"CRAFTY PRIME:", p, TimeUsed[] - start}];
p = NextPrime@p;
If[Mod[++i, 1000000] == 0,
Print[{"Last prime checked:", p, TimeUsed[] - start}]
]
]


I'll keep updating the post, if I find any primes or can think of optimisations.

It currently checks all primes up to 100,000,000 in about 5.5 minutes.

Edit: I decided to follow the OP's example and switched to a lookup table for base conversion. That gave roughly a 30% speedup.

## Crafty Numbers in general

I'm stopping my search for crafty primes now, since I'd need several days just to catch up with where the Perl answer already got. Instead, I started searching for all crafty numbers. Maybe their distribution helps to find a proof the the number of crafty primes is finite or infinite.

I define right-crafty numbers those which divide the related number obtained by interpreting the digit to the right as the new base, and left-crafty numbers accordingly. It will probably help to tackle these individually for a proof.

Here are all left-crafty numbers up to 2,210,000,000:

{2, 5, 16, 28, 68, 160, 222, 280, 555, 680, 777, 1600, 2605, 2800,
6800, 7589, 7689, 9397, 9777, 16000, 16064, 16122, 22222, 24682,
26050, 28000, 55555, 68000, 75890, 76890, 93557, 160000, 160640,
161220, 247522, 254408, 260500, 280000, 680000, 758900, 768900,
949395, 1600000, 1606400, 1612200, 2222222, 2544080, 2605000,
2709661, 2710271, 2717529, 2800000, 3517736, 5555555, 6800000,
7589000, 7689000, 9754696, 11350875, 16000000, 16064000, 16122000,
25440800, 26050000, 27175290, 28000000, 28028028, 35177360, 52623721,
68000000, 68654516, 75890000, 76890000, 113508750, 129129129, 160000000,
160640000, 161220000, 222222222, 254408000, 260500000, 271752900,
275836752, 280000000, 280280280, 333018547, 351773600, 370938016,
555555555, 680000000, 758900000, 768900000, 777777777, 877827179,
1135087500, 1291291290, 1600000000, 1606400000, 1612200000, 1944919449}


And here are all right-crafty numbers in that range:

{2, 5, 16, 28, 68, 125, 128, 175, 222, 284, 555, 777, 1575, 1625,
1875, 3449, 5217, 6287, 9375, 14625, 16736, 19968, 22222, 52990,
53145, 55555, 58750, 93750, 127625, 152628, 293750, 529900, 587500,
593750, 683860, 937500, 1034375, 1340625, 1488736, 2158750, 2222222,
2863740, 2937500, 5299000, 5555555, 5875000, 5937500, 6838600,
7577355, 9375000, 12071125, 19325648, 21587500, 28637400, 29375000,
29811250, 42107160, 44888540, 52990000, 58750000, 59375000, 68386000,
71461386, 74709375, 75773550, 93750000, 100540625, 116382104,
164371875, 197313776, 207144127, 215875000, 222222222, 226071269,
227896480, 274106547, 284284284, 286374000, 287222080, 293750000,
298112500, 421071600, 448885400, 529900000, 555555555, 587500000,
593750000, 600481125, 683860000, 714613860, 747093750, 757735500,
769456199, 777777777, 853796995, 937500000, 1371513715, 1512715127,
1656354715, 1728817288, 1944919449, 2158750000}


Note that there's an infinite number of left-crafty and right-crafty numbers, because there are several ways to generate them from existing ones:

• One can append an arbitrary number of 0s to any left-crafty number whose least significant digit is greater than its most significant digit to obtain another left-crafty number.
• Likewise, one can append an arbitrary number of 0s to any right-crafty number whose least significant digit in less than its most significant digit. This (and the previous statement) is because the 0 will be appended to both the crafty number and its related number, effectively multiplying both of them by 10.
• Every odd number of 2s or 5s is crafty.
• Every odd number of 777s is crafty.
• It appears that an odd number of 28 joined by 0s, like 28028028 is always left-crafty.

Other things to note:

• There are at least four 10-digit numbers which consist of two repeated five-digit numbers (which are themselves not crafty, but there might be some pattern here anyway).
• There are a lot of right-crafty numbers that are a multiple of 125. It might be worth investigating those to find another production rule.
• I haven't found a left-crafty number that starts with 4 or ends with 3.
• Right-crafty numbers can start with any digit but I haven't found a right-crafty number ending in 1 or 3.

I suppose this list would be more interesting if I omitted those whose existence is implied by a smaller crafty number, especially since these are never primes by the construction rules discovered so far. So here are all crafty primes that cannot constructed with one of the above rules:

Left-crafty:
{16, 68, 2605, 7589, 7689, 9397, 9777, 16064, 16122, 24682,
93557, 247522, 254408, 949395, 2709661, 2710271, 2717529, 3517736,
9754696, 11350875, 52623721, 68654516, 129129129, 275836752,
333018547, 370938016, 877827179, 1944919449}

Right-crafty:
{16, 28, 68, 125, 128, 175, 284, 1575, 1625, 1875, 3449, 5217,
6287, 9375, 14625, 16736, 19968, 52990, 53145, 58750, 127625,
152628, 293750, 593750, 683860, 1034375, 1340625, 1488736, 2158750,
2863740, 7577355, 12071125, 19325648, 29811250, 42107160, 44888540,
71461386, 74709375, 100540625, 116382104, 164371875, 197313776,
207144127, 226071269, 227896480, 274106547, 284284284, 287222080,
600481125, 769456199, 853796995, 1371513715, 1512715127, 1656354715,
1728817288, 1944919449}


Note also that there a few doubly-crafty numbers (those which appear in both lists and hence divide both related numbers):

{2, 5, 16, 28, 68, 222, 555, 777, 22222, 55555, 2222222, 5555555, 1944919449}


There exist infinitely many of these as well. But as you can see, except for 16, 28, 68 these all consist only of a single (repeated) digit. It would also be interesting prove whether any larger numbers can be doubly crafty without having that property, but that might just drop out as a corollary of a proof for singly crafty numbers. Found the counter-example 1944919449.

• Is there any reason you have 100540625, 100540625 in your right-crafty list? – isaacg Aug 12 '14 at 18:03
• @isaacg yes. because I can't copy and paste. – Martin Ender Aug 12 '14 at 18:10
• Accepting this since no one found crafty primes beyond 93,557. This was the first answer, is highest voted, and goes into the most depth. – Calvin's Hobbies Nov 1 '14 at 2:50

# Perl (1e5 in 0.03s, 1e8 in 21s). Max 93557 to 1e11.

Very similar to the original. Changes include:

• transpose the base lookup. Small language-dependent savings.
• mod the incremented right shift instead of if. Language dependent micro-opt.
• use Math::GMPz because Perl 5 doesn't have auto-magic bigints like Python and Perl 6.
• Use 2s <= left instead of int(left/s) >= 2. Native integer shift vs. bigint divide.

Does first 1e8 primes in 21 seconds on my fast machine, 3.5 minutes for 1e9, 34 minutes for 1e10. I'm a little surprised it is at all faster than the Python code for small inputs. We could parallelize (Pari/GP's new parforprime would be nice for this). Since it is a search we can parallelize by hand I suppose (forprimes can take two arguments). forprimes is basically like Pari/GP's forprime -- it does segmented sieves internally and calls the block with each result. It's convenient, but for this problem I don't think it is a performance area.

#!/usr/bin/env perl
use warnings;
use strict;
use Math::Prime::Util qw/forprimes/;
use Math::GMPz;

my @rbase = (
[   0,"",       0,   0,  0, 0, 0, 0, 0, 0],
[qw/1 0         1    1   1  1  1  1  1  1/],
[qw/2 00        10   2   2  2  2  2  2  2/],
[qw/3 000       11   10  3  3  3  3  3  3/],
[qw/4 0000      100  11  10 4  4  4  4  4/],
[qw/5 00000     101  12  11 10 5  5  5  5/],
[qw/6 000000    110  20  12 11 10 6  6  6/],
[qw/7 0000000   111  21  13 12 11 10 7  7/],
[qw/8 00000000  1000 22  20 13 12 11 10 8/],
[qw/9 000000000 1001 100 21 14 13 12 11 10/],
);

my($s,$left,$right,$slen,$i,$barray);
forprimes {
($s,$slen,$left,$right) = ($_,length($_),'','');
foreach $i (0 ..$slen-1) {
$barray =$rbase[substr($s,$i,1)];
$left .=$barray->[substr($s,$i-1,1)];
$right .=$barray->[substr($s,($i+1) % $slen,1)]; }$left = Math::GMPz::Rmpz_init_set_str($left,10) if length($left) >= 20;
$right = Math::GMPz::Rmpz_init_set_str($right,10) if length($right) >= 20; print "$s      $left$right\n" if (($s<<1) <=$left && $left %$s == 0)
|| (($s<<1) <=$right && $right %$s == 0);
} 1e9;


# C++11, with threads and GMP

Timing (on a MacBook Air):

• 10^8 in 2.18986s
• 10^9 in 21.3829s
• 10^10 in 421.392s
• 10^11 in 2557.22s
• 10^8 in 3.95095s
• 10^9 in 37.7009s

Requirements:

#include <vector>
#include <iostream>
#include <chrono>
#include <cmath>
#include <future>
#include <mutex>
#include <atomic>
#include "primesieve.hpp"
#include "gmpxx.h"

using namespace std;

using ull = unsigned long long;

mutex cout_mtx;
atomic<ull> prime_counter;

string ppnum(ull number) {
if (number == 0) {
return "0 * 10^0";
}
else {
int l = floor(log10(number));
}
}

inline void bases(int& base, int& digit, mpz_class& sofar) {
switch (base) {
case 0:
sofar *= 10;
sofar += digit;
break;
case 1:
sofar *= pow(10, digit);
break;
case 2:
switch (digit) {
case 0: sofar *= 10; break;
case 1: sofar *= 10; sofar += 1; break;
case 2: sofar *= 100; sofar += 10; break;
case 3: sofar *= 100; sofar += 11; break;
case 4: sofar *= 1000; sofar += 100; break;
case 5: sofar *= 1000; sofar += 101; break;
case 6: sofar *= 1000; sofar += 110; break;
case 7: sofar *= 1000; sofar += 111; break;
case 8: sofar *= 10000; sofar += 1000; break;
case 9: sofar *= 10000; sofar += 1001; break;
}
break;
case 3:
switch (digit) {
case 0: sofar *= 10; break;
case 1: sofar *= 10; sofar += 1; break;
case 2: sofar *= 10; sofar += 2; break;
case 3: sofar *= 100; sofar += 10; break;
case 4: sofar *= 100; sofar += 11; break;
case 5: sofar *= 100; sofar += 12; break;
case 6: sofar *= 100; sofar += 20; break;
case 7: sofar *= 100; sofar += 21; break;
case 8: sofar *= 100; sofar += 22; break;
case 9: sofar *= 1000; sofar += 100; break;
}
break;
case 4:
switch (digit) {
case 0: sofar *= 10; break;
case 1: sofar *= 10; sofar += 1; break;
case 2: sofar *= 10; sofar += 2; break;
case 3: sofar *= 10; sofar += 3; break;
case 4: sofar *= 100; sofar += 10; break;
case 5: sofar *= 100; sofar += 11; break;
case 6: sofar *= 100; sofar += 12; break;
case 7: sofar *= 100; sofar += 13; break;
case 8: sofar *= 100; sofar += 20; break;
case 9: sofar *= 100; sofar += 21; break;
}
break;
case 5:
switch (digit) {
case 0: sofar *= 10; break;
case 1: sofar *= 10; sofar += 1; break;
case 2: sofar *= 10; sofar += 2; break;
case 3: sofar *= 10; sofar += 3; break;
case 4: sofar *= 10; sofar += 4; break;
case 5: sofar *= 100; sofar += 10; break;
case 6: sofar *= 100; sofar += 11; break;
case 7: sofar *= 100; sofar += 12; break;
case 8: sofar *= 100; sofar += 13; break;
case 9: sofar *= 100; sofar += 14; break;
}
break;
case 6:
switch (digit) {
case 0: sofar *= 10; break;
case 1: sofar *= 10; sofar += 1; break;
case 2: sofar *= 10; sofar += 2; break;
case 3: sofar *= 10; sofar += 3; break;
case 4: sofar *= 10; sofar += 4; break;
case 5: sofar *= 10; sofar += 5; break;
case 6: sofar *= 100; sofar += 10; break;
case 7: sofar *= 100; sofar += 11; break;
case 8: sofar *= 100; sofar += 12; break;
case 9: sofar *= 100; sofar += 13; break;
}
break;
case 7:
switch (digit) {
case 0: sofar *= 10; break;
case 1: sofar *= 10; sofar += 1; break;
case 2: sofar *= 10; sofar += 2; break;
case 3: sofar *= 10; sofar += 3; break;
case 4: sofar *= 10; sofar += 4; break;
case 5: sofar *= 10; sofar += 5; break;
case 6: sofar *= 10; sofar += 6; break;
case 7: sofar *= 100; sofar += 10; break;
case 8: sofar *= 100; sofar += 11; break;
case 9: sofar *= 100; sofar += 12; break;
}
break;
case 8:
switch (digit) {
case 0: sofar *= 10; break;
case 1: sofar *= 10; sofar += 1; break;
case 2: sofar *= 10; sofar += 2; break;
case 3: sofar *= 10; sofar += 3; break;
case 4: sofar *= 10; sofar += 4; break;
case 5: sofar *= 10; sofar += 5; break;
case 6: sofar *= 10; sofar += 6; break;
case 7: sofar *= 10; sofar += 7; break;
case 8: sofar *= 100; sofar += 10; break;
case 9: sofar *= 100; sofar += 11; break;
}
break;
case 9:
switch (digit) {
case 0: sofar *= 10; break;
case 1: sofar *= 10; sofar += 1; break;
case 2: sofar *= 10; sofar += 2; break;
case 3: sofar *= 10; sofar += 3; break;
case 4: sofar *= 10; sofar += 4; break;
case 5: sofar *= 10; sofar += 5; break;
case 6: sofar *= 10; sofar += 6; break;
case 7: sofar *= 10; sofar += 7; break;
case 8: sofar *= 10; sofar += 8; break;
case 9: sofar *= 100; sofar += 10; break;
}
break;
};
}

vector<ull> crafty(ull start, ull stop) {
cout_mtx.lock();
cout << "Thread scanning from " << start << " to " << stop << endl;
cout_mtx.unlock();
vector<ull> res;

auto prime_iter = primesieve::iterator(start);
ull num;
int prev, curr, next, fprev;
int i, size;
mpz_class left, right;
unsigned long num_cpy;
unsigned long* num_ptr;
mpz_class num_mpz;

while ((num = prime_iter.next_prime()) && num < stop) {
++prime_counter;
left = 0;
right = 0;
size = floor(log10(num));
i = pow(10, size);
prev = num % 10;
fprev = curr = num / i;
if (i != 1) {
i /= 10;
next = (num / i) % 10;
}
else {
next = prev;
}
for (size += 1; size; --size) {
bases(prev, curr, left);
bases(next, curr, right);
prev = curr;
curr = next;
if (i > 1) {
i /= 10;
next = (num / i) % 10;
}
else {
next = fprev;
}
}
num_cpy = num;

if (num != num_cpy) {
num_ptr = (unsigned long *) &num;
num_mpz = *num_ptr;
num_mpz << sizeof(unsigned long) * 8;
num_mpz += *(num_ptr + 1);
}
else {
num_mpz = num_cpy;
}
if ((left % num_mpz == 0 && left / num_mpz >= 2) || (right % num_mpz == 0 && right / num_mpz >= 2)) {
res.push_back(num);
}
}
cout_mtx.lock();
cout << "Thread scanning from " << start << " to " << stop << " is done." << endl;;
cout << "Found " << res.size() << " crafty primes." << endl;
cout_mtx.unlock();
return res;
}

int main(int argc, char *argv[]) {
ull start = 0, stop = 1000000000;

if (argc > 1) {
start = atoll(argv[1]);
}
if (argc > 2) {
stop = atoll(argv[2]);
}
if (argc > 3) {
}
ull gap = stop - start;

cout << "Start: " << ppnum(start) << ", stop: " << ppnum(stop) << endl;
cout << "Scanning " << ppnum(gap) << " numbers" << endl;

chrono::time_point<chrono::system_clock> tstart, tend;
tstart = chrono::system_clock::now();

cout << "Checking primes..." << endl;

using fur = future<vector<ull>>;

vector<fur> futures;
for (int i = 0; i < number_of_threads; ++i) {
futures.push_back(move(p.get_future()));
auto tstop = (i + 1 == number_of_threads) ? (stop) : (start + gap / number_of_threads * (i + 1));
}

vector<ull> res;

}

for (auto& fut : futures) {
auto v = fut.get();
res.insert(res.end(), v.begin(), v.end());
}

cout << "Finished checking primes..." << endl;

tend = chrono::system_clock::now();
chrono::duration<double> elapsed_seconds = tend - tstart;

cout << "Number of tested primes: " << ppnum(prime_counter) << endl;
cout << "Number of found crafty primes: " << res.size() << endl;
cout << "Crafty primes are: ";
for (auto iter = res.begin(); iter != res.end(); ++iter) {
if (iter != res.begin())
cout << ", ";
cout << *iter;
}
cout << endl;
cout << "Time taken: " << elapsed_seconds.count() << endl;
}


Output:

Start: 0 * 10^0, stop: 1.000000 * 10^11
Scanning 1.000000 * 10^11 numbers
Checking primes...
Thread scanning from 25000000000 to 50000000000
Thread scanning from 0 to 25000000000
Thread scanning from 50000000000 to 75000000000
Thread scanning from 75000000000 to 100000000000
Thread scanning from 75000000000 to 100000000000 is done.
Found 0 crafty primes.
Thread scanning from 50000000000 to 75000000000 is done.
Found 0 crafty primes.
Thread scanning from 25000000000 to 50000000000 is done.
Found 0 crafty primes.
Thread scanning from 0 to 25000000000 is done.
Found 7 crafty primes.
Finished checking primes...
Number of tested primes: 4.118055 * 10^9
Number of found crafty primes: 7
Crafty primes are: 2, 5, 3449, 6287, 7589, 9397, 93557
Time taken: 2557.22

• At num=12919, right should be 120000000001000000000. This overflows a 64-bit int, and in your program r = 9223372036854775807. I think you're going to need to use GMP or something similar. – DanaJ Aug 13 '14 at 5:49
• Very nice. Timing on 3930K with 12 threads is 54s for 1e10 and 1e11 in 421s. – DanaJ Aug 13 '14 at 14:50
• It was a good excuse to try out the concurrency C++11 features – matsjoyce Aug 13 '14 at 15:36

Similar in concept to the rest, with probably a bit of optimizations here and there.

Compile: gcc -I/usr/local/include -Ofast crafty.c -pthread -L/usr/local/lib -lgmp && ./a.out

1e8 in 17 seconds with 1 thread on my macbook air.

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <gmp.h>
#include <string.h>

#define MAX_DIGITS   32768       // Maximum digits allocated for the string... some c stuff
#define MAX_NUMBER   "100000000" // Number in string format
#define START_INDEX  1           // Must be an odd number >= 1
#define GET_WRAP_INDEX(index, stringLength) index<0?stringLength+index:index>=stringLength?index-stringLength:index

static void huntCraftyPrime(int startIndex) {

char lCS [MAX_DIGITS];
char rCS [MAX_DIGITS];
char tPS [MAX_DIGITS];

mpz_t tP, lC, rC, max;
mpz_init_set_ui(tP, startIndex);
mpz_init(lC);
mpz_init(rC);
mpz_init_set_str(max, MAX_NUMBER, 10);

if (START_INDEX < 9 && startIndex == START_INDEX) {
printf("10 10 2\n\n");
printf("10 10 5\n\n");
}

while (mpz_cmp(max, tP) > 0) {
mpz_get_str(tPS, 10, tP);
int tPSLength = strlen(tPS);
int l = 0, r = 0, p = 0;
while (p < tPSLength) {
char lD = tPS[GET_WRAP_INDEX(p-1, tPSLength)];
char d  = tPS[GET_WRAP_INDEX(p  , tPSLength)];
char rD = tPS[GET_WRAP_INDEX(p+1, tPSLength)];
if (d == '0') {
if (lD != '1') lCS[l++] = '0';
if (rD != '1') rCS[r++] = '0';
} else if (d == '1') {
lCS[l++] = (lD != '1') ? '1' : '0';
rCS[r++] = (rD != '1') ? '1' : '0';
} else if (d == '2') {
if (lD == '1') {
lCS[l++] = '0';
lCS[l++] = '0';
} else if (lD == '2') {
lCS[l++] = '1';
lCS[l++] = '0';
} else {
lCS[l++] = '2';
}
if (rD == '1') {
rCS[r++] = '0';
rCS[r++] = '0';
} else if (rD == '2') {
rCS[r++] = '1';
rCS[r++] = '0';
} else {
rCS[r++] = '2';
}
} else if (d == '3') {
if (lD == '1') {
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
} else if (lD == '2') {
lCS[l++] = '1';
lCS[l++] = '1';
} else if (lD == '3') {
lCS[l++] = '1';
lCS[l++] = '0';
} else {
lCS[l++] = '3';
}
if (rD == '1') {
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
} else if (rD == '2') {
rCS[r++] = '1';
rCS[r++] = '1';
} else if (rD == '3') {
rCS[r++] = '1';
rCS[r++] = '0';
} else {
rCS[r++] = '3';
}
} else if (d == '4') {
if (lD == '1') {
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
} else if (lD == '2') {
lCS[l++] = '1';
lCS[l++] = '0';
lCS[l++] = '0';
} else if (lD == '3') {
lCS[l++] = '1';
lCS[l++] = '1';
} else if (lD == '4') {
lCS[l++] = '1';
lCS[l++] = '0';
} else {
lCS[l++] = '4';
}
if (rD == '1') {
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
} else if (rD == '2') {
rCS[r++] = '1';
rCS[r++] = '0';
rCS[r++] = '0';
} else if (rD == '3') {
rCS[r++] = '1';
rCS[r++] = '1';
} else if (rD == '4') {
rCS[r++] = '1';
rCS[r++] = '0';
} else {
rCS[r++] = '4';
}
} else if (d == '5') {
if (lD == '1') {
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
} else if (lD == '2') {
lCS[l++] = '1';
lCS[l++] = '0';
lCS[l++] = '1';
} else if (lD == '3') {
lCS[l++] = '1';
lCS[l++] = '2';
} else if (lD == '4') {
lCS[l++] = '1';
lCS[l++] = '1';
} else if (lD == '5') {
lCS[l++] = '1';
lCS[l++] = '0';
} else {
lCS[l++] = '5';
}
if (rD == '1') {
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
} else if (rD == '2') {
rCS[r++] = '1';
rCS[r++] = '0';
rCS[r++] = '1';
} else if (rD == '3') {
rCS[r++] = '1';
rCS[r++] = '2';
} else if (rD == '4') {
rCS[r++] = '1';
rCS[r++] = '1';
} else if (rD == '5') {
rCS[r++] = '1';
rCS[r++] = '0';
} else {
rCS[r++] = '5';
}
} else if (d == '6') {
if (lD == '1') {
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
} else if (lD == '2') {
lCS[l++] = '1';
lCS[l++] = '1';
lCS[l++] = '0';
} else if (lD == '3') {
lCS[l++] = '2';
lCS[l++] = '0';
} else if (lD == '4') {
lCS[l++] = '1';
lCS[l++] = '2';
} else if (lD == '5') {
lCS[l++] = '1';
lCS[l++] = '1';
} else if (lD == '6') {
lCS[l++] = '1';
lCS[l++] = '0';
} else {
lCS[l++] = '6';
}
if (rD == '1') {
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
} else if (rD == '2') {
rCS[r++] = '1';
rCS[r++] = '1';
rCS[r++] = '0';
} else if (rD == '3') {
rCS[r++] = '2';
rCS[r++] = '0';
} else if (rD == '4') {
rCS[r++] = '1';
rCS[r++] = '2';
} else if (rD == '5') {
rCS[r++] = '1';
rCS[r++] = '1';
} else if (rD == '6') {
rCS[r++] = '1';
rCS[r++] = '0';
} else {
rCS[r++] = '6';
}
} else if (d == '7') {
if (lD == '1') {
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
} else if (lD == '2') {
lCS[l++] = '1';
lCS[l++] = '1';
lCS[l++] = '1';
} else if (lD == '3') {
lCS[l++] = '2';
lCS[l++] = '1';
} else if (lD == '4') {
lCS[l++] = '1';
lCS[l++] = '3';
} else if (lD == '5') {
lCS[l++] = '1';
lCS[l++] = '2';
} else if (lD == '6') {
lCS[l++] = '1';
lCS[l++] = '1';
} else if (lD == '7') {
lCS[l++] = '1';
lCS[l++] = '0';
} else {
lCS[l++] = '7';
}
if (rD == '1') {
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
} else if (rD == '2') {
rCS[r++] = '1';
rCS[r++] = '1';
rCS[r++] = '1';
} else if (rD == '3') {
rCS[r++] = '2';
rCS[r++] = '1';
} else if (rD == '4') {
rCS[r++] = '1';
rCS[r++] = '3';
} else if (rD == '5') {
rCS[r++] = '1';
rCS[r++] = '2';
} else if (rD == '6') {
rCS[r++] = '1';
rCS[r++] = '1';
} else if (rD == '7') {
rCS[r++] = '1';
rCS[r++] = '0';
} else {
rCS[r++] = '7';
}
} else if (d == '8') {
if (lD == '1') {
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
} else if (lD == '2') {
lCS[l++] = '1';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
} else if (lD == '3') {
lCS[l++] = '2';
lCS[l++] = '2';
} else if (lD == '4') {
lCS[l++] = '2';
lCS[l++] = '0';
} else if (lD == '5') {
lCS[l++] = '1';
lCS[l++] = '3';
} else if (lD == '6') {
lCS[l++] = '1';
lCS[l++] = '2';
} else if (lD == '7') {
lCS[l++] = '1';
lCS[l++] = '1';
} else if (lD == '8') {
lCS[l++] = '1';
lCS[l++] = '0';
} else {
lCS[l++] = '8';
}
if (rD == '1') {
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
} else if (rD == '2') {
rCS[r++] = '1';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
} else if (rD == '3') {
rCS[r++] = '2';
rCS[r++] = '2';
} else if (rD == '4') {
rCS[r++] = '2';
rCS[r++] = '0';
} else if (rD == '5') {
rCS[r++] = '1';
rCS[r++] = '3';
} else if (rD == '6') {
rCS[r++] = '1';
rCS[r++] = '2';
} else if (rD == '7') {
rCS[r++] = '1';
rCS[r++] = '1';
} else if (rD == '8') {
rCS[r++] = '1';
rCS[r++] = '0';
} else {
rCS[r++] = '8';
}
} else if (d == '9') {
if (lD == '1') {
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '0';
} else if (lD == '2') {
lCS[l++] = '1';
lCS[l++] = '0';
lCS[l++] = '0';
lCS[l++] = '1';
} else if (lD == '3') {
lCS[l++] = '1';
lCS[l++] = '0';
lCS[l++] = '0';
} else if (lD == '4') {
lCS[l++] = '2';
lCS[l++] = '1';
} else if (lD == '5') {
lCS[l++] = '1';
lCS[l++] = '4';
} else if (lD == '6') {
lCS[l++] = '1';
lCS[l++] = '3';
} else if (lD == '7') {
lCS[l++] = '1';
lCS[l++] = '2';
} else if (lD == '8') {
lCS[l++] = '1';
lCS[l++] = '1';
} else if (lD == '9') {
lCS[l++] = '1';
lCS[l++] = '0';
} else {
lCS[l++] = '9';
}
if (rD == '1') {
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '0';
} else if (rD == '2') {
rCS[r++] = '1';
rCS[r++] = '0';
rCS[r++] = '0';
rCS[r++] = '1';
} else if (rD == '3') {
rCS[r++] = '1';
rCS[r++] = '0';
rCS[r++] = '0';
} else if (rD == '4') {
rCS[r++] = '2';
rCS[r++] = '1';
} else if (rD == '5') {
rCS[r++] = '1';
rCS[r++] = '4';
} else if (rD == '6') {
rCS[r++] = '1';
rCS[r++] = '3';
} else if (rD == '7') {
rCS[r++] = '1';
rCS[r++] = '2';
} else if (rD == '8') {
rCS[r++] = '1';
rCS[r++] = '1';
} else if (rD == '9') {
rCS[r++] = '1';
rCS[r++] = '0';
} else {
rCS[r++] = '9';
}
}
++p;
}
lCS[l] = '\0';
rCS[r] = '\0';

mpz_set_str(lC, lCS, 10);
mpz_set_str(rC, rCS, 10);

if ((mpz_divisible_p(lC, tP) && mpz_cmp(lC, tP) > 0) || (mpz_divisible_p(rC, tP) && mpz_cmp(rC, tP) > 0)){
if (mpz_millerrabin(tP, 25)) {
gmp_printf("%Zd %Zd %Zd\n\n", lC, rC, tP);
}
}
}
}

int* startIndex = (int*) p;
huntCraftyPrime(*startIndex);
}

int main(int argc, char *argv[]) {

struct timeval time_started, time_now, time_diff;
gettimeofday(&time_started, NULL);

int startIndex = START_INDEX;
for (int i = 0; i < THREAD_COUNT; ++i) {
for (;startIndex % 2 == 0; ++startIndex);
startIndexes[i] = startIndex;
if (rc) {
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
++startIndex;
}

for (int i = 0; i < THREAD_COUNT; ++i) {
void * status;
if (rc) {
printf("ERROR: return code from pthread_join() is %d\n", rc);
exit(-1);
}
}

gettimeofday(&time_now, NULL);
timersub(&time_now, &time_started, &time_diff);
printf("Time taken,%ld.%.6d s\n", time_diff.tv_sec, time_diff.tv_usec);

return 0;
}


# Python, finds 93557 in 0.28s

Very similar to OP's code in that it also uses pyprimes. I did write this myself though xD

import pyprimes, time

d = time.clock()

def to_base(base, n):
if base == 1:
return '0'*n
s = ""
while n:
s = str(n % base) + s
n //= base
return s

def crafty(n):
digits = str(n)
l, r = "", ""
for i in range(len(digits)):
t = int(digits[i])
base = int(digits[i-1])
l += to_base(base, t) if base else digits[i]
base = int(digits[(i+1)%len(digits)])
r += to_base(base, t) if base else digits[i]
l, r = int(l) if l else 0, int(r) if r else 0
if (l%n==0 and 2 <= l/n) or (r%n==0 and 2 <= r/n):
print(n, l, r, time.clock()-d)

for i in pyprimes.primes_above(1):
crafty(i)


It also prints out the time since start that it finds a number.

Output:

2 10 10 3.156656792490237e-05
5 10 10 0.0006756015452219958
3449 3111021 3104100 0.012881854420378145
6287 6210007 11021111 0.022036544076745254
7589 751311 125812 0.026288406792971432
9397 1231007 1003127 0.03185028207808106
93557 123121012 10031057 0.27897531840850603


Format is number left right time. As a comparison, OP's code finds 93557 in around 0.37.