!empty($v) && !is_numeric($v[0])); } public function __construct(string $expression = "", array $input_vars = []) { $this->setExpression($expression); $this->setInputVars(($input_vars === []) ? self::collectVariables($this->expression) : $input_vars); } public function getTruthTable(): string { $N = count($this->input_vars); if ($N == 0) { return ""; } $M = pow(2, $N); $vars = []; foreach ($this->input_vars as $var) { $vars[$var] = 0; } $expression = $this->getExpression("verilog_logic"); // printf("Cooked: %s\n", $cooked_form); $tt = []; for ($i = 0; $i < $M; $i++) { for ($k = 0; $k < $N; $k++) { $vars[$this->input_vars[$k]] = (($i >> ($N - $k - 1)) & 1) === 1; // printf("%d ", $vars[$this->input_vars[$k]]); } $out = self::$EXP_LANG->evaluate($expression, $vars); $out_str = $out ? "1" : "0"; // printf("%d\n", $out); $tt[] = $out_str; } return join("", $tt); } public static function genTerm(array $vars, int $ftn, int $tn, int $mind, int $maxd, bool $top = true, int $opindex = 1): string { $term = ""; $m = max($ftn, random_int(1, $tn)); if ((($maxd === 0) || ($m === 1)) && ($ftn === 0)) { $neg = random_int(0, 1) === 1; $var = $vars[array_rand($vars, 1)]; $term = ($neg ? "~" : "") . $var; } else { $depth = random_int(0, max(0, $maxd - 1)); $verilog_ops = [" & ", " | "]; $verilog_op = $verilog_ops[$opindex]; $term = !$top ? "(" : ""; $nextopindex = ($opindex === 0) ? 1 : 0; for ($i = 0; $i < $m; $i++) { $subterm = self::genTerm($vars, (($mind - 1) > 0) ? $ftn : 0, $tn, $mind - 1, $depth, false, $nextopindex); $term .= $subterm; if ($i < $m - 1) { $term .= $verilog_op; } } $term .= !$top ? ")" : ""; } return $term; } public static function genRandom(array $input_vars, int $min_depth = 2, int $max_depth = 3): LogicFunction { $term = self::genTerm($input_vars, count($input_vars), count($input_vars), $min_depth, $max_depth); return new LogicFunction($term, $input_vars); } public static function genRandomDNF($input_vars): LogicFunction { $N = count($input_vars); $states = pow(2, $N); $verilog_term = ""; for ($i = 0; $i < $states; $i++) { $inside = ""; $omit = random_int(0, 1); // omit the term or not? if (!$omit) { for ($j = 0; $j < $N; $j++) { $neg = !($i & (1 << $j)); // is it an inverted variable? $term = $input_vars[$j]; if ($neg) { $inside .= "~" . $term; } else { $inside .= $term; } if ($j < ($N - 1)) { $inside .= " & "; } } } if ($inside !== "") { $verilog_term .= "("; $verilog_term .= $inside; $verilog_term .= ")"; if (($i < ($states - 1)) && !$omit) { $verilog_term .= " | "; } } } $verilog_term = rtrim($verilog_term, "| "); return new LogicFunction($verilog_term, $input_vars); } public function setExpression(string $expression): void { $this->expression = self::convertToVerilogBitwiseForm($expression); } public function getExpression(string $fmt = "verilog_bitwise"): string { switch (strtolower($fmt)) { case "verilog_logic": return str_replace(["&", "|", "~"], ["&&", "||", "!"], $this->expression); case "tex": { $tex_form = str_replace([" | ", " & ", "(", ")"], [" + ", " \\cdot ", "\\left(", "\\right)"], $this->expression); return preg_replace("/~([a-zA-Z0-9_])/", '\\overline{$1}', $tex_form); } default: case "verilog_bitwise": return $this->expression; } } public function getInputVars(): array { return $this->input_vars; } public function setInputVars(array $input_vars): void { $this->input_vars = $input_vars; } public function getNStates(): int { return pow(2, count($this->input_vars)); } public function isValid(): bool { try { self::$EXP_LANG->lint($this->expression, $this->input_vars); } catch (Exception $e) { return false; } return true; } public function toArray(): array { return ["expression" => $this->expression, "input_vars" => $this->input_vars]; } public function jsonSerialize() { return $this->toArray(); } public static function fromArray(array $a): LogicFunction { return new LogicFunction($a["expression"] ?? "", $a["input_vars"] ?? []); } // general minterm regex: ([/!~]*[a-zA-Z_][a-zA-Z0-9_]*[&]{1,2})*([/!~]*[a-zA-Z_][a-zA-Z0-9_]*) // specific regex: ([\/!~]*()[&]{1,2})*([\/!~]*()) public static function isCorrectDNF(array $input_vars, string $exp): bool { $exp = trim($exp); // trim spaces $minterms = explode("|", $exp); // break up the expression into minterms $minterms = array_map(fn($mt) => trim($mt, " ()\t"), $minterms); // strip the parentheses off the minterms $minterms = array_map(fn($mt) => str_replace(" ", "", $mt), $minterms); // remove spaces $ivars = implode("|", $input_vars); // create | separated list of input vars to be used with the regular expression $regex = "/([\/!~]*(${ivars})[&]{1,2})*([\/!~]*(${ivars}))/"; // specific regular expression foreach ($minterms as $minterm) { if (preg_match($regex, $minterm) !== 1) { // generally try to match the minterm return false; } preg_match_all("/[\/!~]*(${ivars})[&]*/", $minterm, $matches); // fetch variables $vars = $matches[1] ?? []; sort($vars); // sort detected variables sort($input_vars); // sort input variables if ($vars !== $input_vars) { // ensure each variable occurs just once return false; } } return true; } public static function initStatic(): void { self::$EXP_LANG = new ExpressionLanguage(); } public function toDNF(): string { $N = count($this->input_vars); $M = pow(2, count($this->input_vars)); $tt = $this->getTruthTable(); $minterms = []; for ($i = 0; $i < $M; $i++) { $r = $tt[$i]; if ($r == "1") { $term = "("; for ($j = 0; $j < $N; $j++) { $inv = (($i >> ($N - $j - 1)) & 1) ? "~" : ""; $term .= $inv . $this->input_vars[$j]; if ($j < ($N - 1)) { $term .= " & "; } } $term .= ")"; $minterms[] = $term; } } return join(" | ", $minterms); } public function drawNetwork(string $outvar = "f"): string { $expr = str_replace(["^"], [" xor "], $this->getExpression()); return PythonUtils::execPy("draw_logic_network.py", [ $expr, $outvar ]); } } LogicFunction::initStatic();