<?php
define('STOP_CHAR', '.');
/*
* GRAMMAR
* arrows = simple | simple ('=' | '<=' | '=>' | '<=>') simple
* simple = negation | negation ('+' | '*' | '#' | '$' | '^') negation
* negation = ('-' | ) paren
* paren = '(' simple ')' | variable
* variable = '0' | '1' | 'a' | 'b' | ... | 'z' | 'A' | ... | 'Z'
*/
function read_variable(&$eq, &$vars)
{
$varname = $eq{0};
if ($varname != '0' && $varname != '1' && !ctype_alpha($eq{0})) return NULL;
$eq = substr($eq, 1);
if ($varname == '0') return false;
if ($varname == '1') return true;
if (isset($vars[$varname])) return $vars[$varname];
return $varname . '?';
}
function read_paren(&$eq, &$vars)
{
if ($eq{0} == '(')
{
$ch = $eq{0}; $eq = substr($eq, 1);
$val = read_arrows($eq, $vars);
if (is_bool($val)) // Valid result
{
if ($eq{0} != ')') return NULL;
$eq = substr($eq, 1);
}
return $val;
}
return read_variable($eq, $vars);
}
function read_negation(&$eq, &$vars)
{
$negate = ($eq{0} == '-');
if ($negate) $eq = substr($eq, 1);
$val = read_paren($eq, $vars);
if ($negate && is_bool($val)) return !$val;
return $val;
}
function read_simple(&$eq, &$vars)
{
$val1 = read_negation($eq, $vars);
$op = $eq{0};
if (!is_bool($val1) || ( $op != '*' && $op != '+' && $op != '$' && $op != '#' && $op != '^')) return $val1;
$eq = substr($eq, 1);
$val2 = read_simple($eq, $vars);
if (!is_bool($val2)) return $val2;
switch ($op)
{
case '*': return $val1 && $val2;
case '+': return $val1 || $val2;
case '#': return !($val1 || $val2);
case '$': return !($val1 && $val2);
case '^': return $val1 xor $val2;
}
}
function read_arrows(&$eq, &$vars)
{
$val1 = read_simple($eq, $vars);
$op = $eq{0};
if (!is_bool($val1) || ( $op != '=' && $op != '<' )) return $val1;
$eq = substr($eq, 1);
while ($eq{0} == '=' || $eq{0} == '>')
{
$op .= $eq{0};
$eq = substr($eq, 1);
}
if ($op != '=' && $op != '<=' && $op != '=>' && $op != '<=>')
return 'Invalid operator "' . $op . '".';
$val2 = read_simple($eq, $vars);
if (!is_bool($val2)) return $val2;
switch ($op)
{
case '=':
case '<=>':
return $val1 == $val2;
case '=>':
return (!$val1) || $val2;
case '<=':
return (!$val2) || $val1;
}
return 'Internal error.';
}
function calculate(&$eq, &$vars)
{
$eq .= STOP_CHAR;
$val = read_arrows($eq, $vars);
if (is_bool($val))
{
if ($eq{0} != STOP_CHAR) return NULL;
}
return $val;
}
function print_table($columns, $unknowns, $latex)
{
$rowcount = pow(2, count($unknowns));
if ($rowcount <= 0) return;
if ($latex)
{
print("<pre>\\begin{displaymath}\n\\begin{array}{");
for ($i = count($columns); $i; $i--) print('|c');
print("}\n ");
}
else print('<table cellspacing="0"><tr>');
$repl_search = array('-', '+', '*', '<=>', '=>', '<=', ':');
$repl_replace = $latex ?
array('\lnot{}', '\lor{}', '\land{}', '\Leftrightarrow{}', '\Rightarrow{}', '\Leftarrow{}', ': ') :
array('¬', '∨', '∧', '⇔', '⇒', '⇐', ': ');
$true_count = array();
$false_count = array();
$error_count = array();
$first_col = true;
foreach ($columns as $eq)
{
if ($latex)
{
if ($first_col) $first_col = false;
else print("\n & ");
}
else print('<th>');
print(str_replace($repl_search, $repl_replace, $eq));
$true_count[] = 0;
$false_count[] = 0;
$error_count[] = 0;
if (!$latex) print('</th>');
}
if ($latex) print(" \\\\\n\\hline\n");
else print('</tr>');
for ($bits = 0; $bits < $rowcount; $bits++)
{
if (!$latex) print('<tr>');
$vars = array();
$x = $bits;
for ($i = count($unknowns)-1; $i >= 0; $i--)
{
$vars[$unknowns[$i]] = (bool) ($x & 0x01);
$x >>= 1;
}
$first_col = true;
foreach ($columns as $col_index => $str)
{
if (strlen($str) > 2 && ctype_alpha($str{0}) && $str{1} == ':')
{
$assign_to_var = $str{0};
$str = substr($str, 2);
}
else $assign_to_var = '';
$val = calculate($str, $vars);
if ($latex)
{
if ($first_col) $first_col = false;
else print(' & ');
}
else
{
print('<td nowrap="nowrap"');
if (($bits & 0x03) == 0x03) print(' class="btmline"');
print('>');
}
if (is_bool($val))
{
print($val ? '1' : '0');
if ($val) $true_count[$col_index]++;
else $false_count[$col_index]++;
}
else
{
if (is_null($val)) $val = 'Error at: ' . $str;
if ($latex) print(htmlspecialchars('\mathrm{' . str_replace(' ', '\ ', $val) . '}'));
else print(htmlspecialchars($val));
$error_count[$col_index]++;
}
if (!$latex) print('</td>');
if ($assign_to_var) $vars[$assign_to_var] = is_bool($val) ? $val : NULL;
}
if ($latex)
{
print(" \\\\\n");
if (($bits & 0x03) == 0x03) print("\\hline\n");
}
if (!$latex) print('</tr>');
}
if ($latex) print("\\end{array}\n\\end{displaymath}\n");
else print('</table>');
foreach ($columns as $col_index => $eq)
{
if ($true_count[$col_index] == $rowcount)
$str = 'Given the equations above, %s is always true.';
else if ($false_count[$col_index] == $rowcount)
$str = 'Given the equations above, %s is always false.';
else if ($error_count[$col_index])
$str = 'Could not evaluate %s. Check the syntax and variable names.';
else continue;
if ($latex) printf("\n$str\n", '$' . str_replace($repl_search, $repl_replace, $eq) . '$');
else printf("<p>$str</p>\n", str_replace($repl_search, $repl_replace, $eq));
}
if ($latex) print('</pre>');
}
function handle_input($input, $latex)
{
$unknowns = array();
$columns = array();
$str = preg_replace('{//.*?$}m', "", $input);
preg_match_all('{[]\-^$#[><=/|&+*()01A-Za-z:]+}', $str, $matches, PREG_SET_ORDER);
foreach ($matches as $eq)
{
$eq = $eq[0];
$columns[] = $eq;
if (strlen($eq) == 1) $unknowns[] = $eq;
}
if (count($columns) == 0) print('<p>No equations or unknowns specified.</p>');
else if (count($unknowns) == 0) print('<p>No unknowns specified.</p>');
else print_table($columns, $unknowns, $latex);
}
preferences:
40.93 ms | 402 KiB | 5 Q