digits: a C program to help you solve digits puzzles.
/*
digits by D. Constales (dcons@world.std.com).
Did you realize that 6 * 3 ^ 9 - 54 * 2187 = 0 ?
This program can help you find such interesting arithmetical
combinations which involve each of the digits 0,1,...,9 exactly once.
Compile this C program and run it e.g. by
./digits '.*.^.-...*....'
to get:
.*.^.-...*.... 6*3^9-054*2187
i.e. the equation A*B^C-DEF*GHIJ=0 has only one solution if the letters
must be the ten digits in some order.
You can use + - * / ^ ( and ) in the expressions. The digits are
represented by dots. Try out e.g. (.+..)^.-......, etc.
Warning: the answers digit provides are *suggestions* in that
integer overflow is not acted upon (to prevent speed penalties). If
a solution includes large intermediate numbers, make sure to check it
using a multi-precision tool like bc!
*/
#include <ctype.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
static char const *prog; /* name of this program (for error msgs) */
static char const *text; /* text of current problem */
static char const *cur; /* cursor into this text for scan/parse */
static jmp_buf jmpbuf; /* jump buffer to abort parsing at errors */
static char digits[10]; /* array of chars to be permuted */
typedef unsigned char token_t; /* type for tokens and ops */
/* ops part */
/* opcodes for the supported operations and for tokens like ( and ) */
#define ADD ((token_t)11)
#define SUB ((token_t)12)
#define MUL ((token_t)13)
#define DIV ((token_t)14)
#define POW ((token_t)15)
#define CHS ((token_t)16)
#define LPR ((token_t)17)
#define RPR ((token_t)18)
/* max number of ops allowed per problem */
#define MXOPS 100
/* the tokens of this problem (for printing out the answers) */
static token_t tks[MXOPS];
static int nxtk;
/* the ops of this problem */
static token_t ops[MXOPS];
static int nxtop;
/* add a token if therés space left */
static void
addtok (token_t const v)
{
if (nxtk >= MXOPS)
{
fprintf (stderr,
"%s: \"%s\": error (at position %d): too many tokens.\n",
prog, text, cur - text);
longjmp (jmpbuf, 1);
}
else
tks[nxtk++] = v;
}
/* add an op if therés space left */
static void
addop (token_t const v)
{
if (nxtop >= MXOPS)
{
fprintf (stderr,
"%s: \"%s\": error (at position %d): too many operations.\n",
prog, text, cur - text);
longjmp (jmpbuf, 1);
}
else
ops[nxtop++] = v;
}
/* integer power, ignoring overflows */
static int
mypow (int x, int y)
{
int r = 1;
while (y > 0)
{
if (y & 1)
r *= x;
y >>= 1;
x *= x;
}
return r;
}
/* compute the value of a sequence of ops (given the current permutation
of digits) */
static int
opexec (void)
{
int j = 0;
token_t *p = ops;
int stack[MXOPS];
int stktop = -1;
while (1)
{
int w = 0;
switch (*p++)
{
case 0:
return stack[0];
case 10:
w = digits[j++];
case 9:
w = 10 * w + digits[j++];
case 8:
w = 10 * w + digits[j++];
case 7:
w = 10 * w + digits[j++];
case 6:
w = 10 * w + digits[j++];
case 5:
w = 10 * w + digits[j++];
case 4:
w = 10 * w + digits[j++];
case 3:
w = 10 * w + digits[j++];
case 2:
w = 10 * w + digits[j++];
case 1:
stack[++stktop] = 10 * w + digits[j++];
break;
case ADD:
stack[stktop - 1] += stack[stktop];
stktop--;
break;
case SUB:
stack[stktop - 1] -= stack[stktop];
stktop--;
break;
case MUL:
stack[stktop - 1] *= stack[stktop];
stktop--;
break;
case DIV:
if (stack[stktop] == 0)
return -12345;
stack[stktop - 1] /= stack[stktop];
stktop--;
break;
case POW:
stack[stktop - 1] = mypow (stack[stktop - 1],
stack[stktop]);
stktop--;
break;
case CHS:
stack[stktop] = -stack[stktop];
break;
default:; /* cannot happen */
}
}
return stack[0];
}
/* parser part */
/* current token */
static token_t tok;
/* total number of dots encountered */
static int dots;
/* scan next token from text at cur */
static void
scan (void)
{
while (isspace (*cur))
cur++;
switch (*cur++)
{
case '\0':
tok = 0;
cur--;
break;
case '+':
tok = ADD;
break;
case '-':
tok = SUB;
break;
case '*':
tok = MUL;
break;
case '/':
tok = DIV;
break;
case '^':
tok = POW;
break;
case '(':
tok = LPR;
break;
case ')':
tok = RPR;
break;
case '.':
tok = 1;
while (*cur == '.')
{
cur++;
tok++;
}
dots += tok;
if (dots > 10)
{
fprintf (stderr,
"%s: \"%s\": error (at position %d): too many \"...\".\n",
prog, text, cur - text);
longjmp (jmpbuf, 1);
}
break;
default:
fprintf (stderr,
"%s: \"%s\": error (at position %d): unknown character \"%c\".\n",
prog, text, cur - text, cur[-1]);
longjmp (jmpbuf, 1);
}
addtok (tok);
}
/* need this declaration for mutual recursiveness */
static void readpm (void);
/* read a kernel (i.e. dots or parenthesised expression) */
static void
readk (void)
{
switch (tok)
{
case LPR:
scan ();
readpm ();
if (tok != RPR)
{
fprintf (stderr,
"%s: \"%s\": error (at position %d): missing closing parenthesis.\n",
prog, text, cur - text);
longjmp (jmpbuf, 1);
}
scan ();
break;
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
addop (tok);
scan ();
break;
default:
fprintf (stderr,
"%s: \"%s\": error (at position %d): syntax error.\n",
prog, text, cur - text);
longjmp (jmpbuf, 1);
}
}
/* read a sequence of exponentials */
static void
readex (void)
{
readk ();
while (tok == POW)
{
scan ();
readk ();
addop (POW);
}
}
/* read a sequence of products and quotients */
static void
readmq (void)
{
readex ();
while (tok == MUL || tok == DIV)
{
int s = tok == MUL;
scan ();
readex ();
if (s == 1)
addop (MUL);
else
addop (DIV);
}
}
/* read a sequence of additions and subtractions */
static void
readpm (void)
{
int first = 1;
do
{
int s = 1;
while (tok == ADD || tok == SUB)
{
if (tok == SUB)
s = -s;
scan ();
}
readmq ();
if (first)
{
if (s == -1)
addop (CHS);
}
else
{
if (s == -1)
addop (SUB);
else
addop (ADD);
}
first = 0;
}
while (tok == ADD || tok == SUB);
}
/* parse an expression */
static void
parse (void)
{
readpm ();
if (tok != 0)
{
fprintf (stderr,
"%s: \"%s\": error (at position %d): trailing garbage.\n",
prog, text, cur - text);
longjmp (jmpbuf, 1);
}
if (dots < 10)
{
fprintf (stderr,
"%s: \"%s\": error (at position %d): not enough \"...\".\n",
prog, text, cur - text);
longjmp (jmpbuf, 1);
}
addop (0);
}
/* permutation part */
/* test if a permutation satisfies the current problem
(i.e. the ops yield 0 for it); print if found */
static void
testit (void)
{
static int counter = 0;
static int ival = 10000;
static time_t start;
if (counter == 0)
time (&start);
counter++;
if (counter % ival == 0)
{
time_t now = time (NULL);
double delta = difftime (now, start);
if (delta == 0)
delta = 1;
fprintf (stderr, "%s: \"%s\": %7d (%5.2f%%)",
prog, text, counter,
counter * 100.0 / 3628800);
if (delta >= 4)
fprintf (stderr, " %7d/s %4ds left.\n",
(int) (counter / delta),
(int) ((3628800.0 / counter - 1) * delta));
else
fprintf (stderr, ".\n");
if (ival < counter / delta)
ival *= 10;
else if (ival > 20 * counter / delta)
ival /= 10;
}
if (0 == opexec ())
{
int i;
int j = 0;
printf ("%s ", text);
for (i = 0; i < nxtk; i++)
switch (tks[i])
{
case 0:
break;
case 10:
putchar (digits[j++] + '0');
case 9:
putchar (digits[j++] + '0');
case 8:
putchar (digits[j++] + '0');
case 7:
putchar (digits[j++] + '0');
case 6:
putchar (digits[j++] + '0');
case 5:
putchar (digits[j++] + '0');
case 4:
putchar (digits[j++] + '0');
case 3:
putchar (digits[j++] + '0');
case 2:
putchar (digits[j++] + '0');
case 1:
putchar (digits[j++] + '0');
break;
case ADD:
putchar ('+');
break;
case SUB:
putchar ('-');
break;
case MUL:
putchar ('*');
break;
case DIV:
putchar ('/');
break;
case POW:
putchar ('^');
break;
case LPR:
putchar ('(');
break;
case RPR:
putchar (')');
break;
}
putchar ('\n');
}
}
/* permute l chars starting at position p */
static void
doperm (char p[], int const l)
{
int i;
for (i = 0; i < l; i++)
{
char tmp;
if (i > 0)
{
tmp = p[0];
p[0] = p[i];
p[i] = tmp;
}
if (l > 2)
doperm (p + 1, l - 1);
else
testit ();
if (i > 0)
{
p[i] = p[0];
p[0] = tmp;
}
}
}
/* solve one given problem; parse errors longjmp back into here */
static void
doit (char const s[])
{
int i;
for (i = 0; i < 10; i++)
digits[i] = i;
if (0 == setjmp (jmpbuf))
{
text = s;
cur = text;
dots = 0;
nxtk = 0;
nxtop = 0;
scan ();
parse ();
doperm (digits, 10);
}
}
/* solve each of the problems from the command line */
int
main (int argc, char *argv[])
{
int i;
prog = argv[0];
for (i = 1; i < argc; i++)
doit (argv[i]);
return 0;
}
Denis Constales - dcons@world.std.com
- http://cage.UGent.be/~dc/index-world.html