<commit_before><?php

namespace igorw\reasoned;

// microKanren implementation

class Variable {
    public $name;
    function __construct($name) {
        $this->name = $name;
    }
    function is_equal(Variable $var) {
        return $this->name === $var->name;
    }
}

function variable($name) {
    return new Variable($name);
}

function is_variable($x) {
    return $x instanceof Variable;
}

class Substitution {
    public $values;
    function __construct(array $values = []) {
        $this->values = $values;
    }
    function walk($u) {
        if (is_variable($u) && $value = $this->find($u)) {
            return $this->walk($value);
        }
        return $u;
    }
    function find(Variable $var) {
        foreach ($this->values as list($x, $value)) {
            if ($var->is_equal($x)) {
                return $value;
            }
        }
        return null;
    }
    function extend(Variable $x, $value) {
        return new Substitution(array_merge(
            [[$x, $value]],
            $this->values
        ));
    }
    function length() {
        return count($this->values);
    }
    function reify($v) {
        $v = $this->walk($v);
        if (is_variable($v)) {
            $n = reify_name($this->length());
            return $this->extend($v, $n);
        }
        if (is_pair($v)) {
            return $this->reify(first($v))
                        ->reify(rest($v));
        }
        return $this;
    }
}

class State {
    public $subst;
    public $count;
    function __construct(Substitution $subst = null, $count = 0) {
        $this->subst = $subst ?: new Substitution();
        $this->count = $count;
    }
    function next() {
        return new State($this->subst, $this->count + 1);
    }
    function reify() {
        $v = walk_star(variable(0), $this->subst);
        return walk_star($v, (new Substitution())->reify($v));
    }
}

function eq($u, $v) {
    return function (State $state) use ($u, $v) {
        $subst = unify($u, $v, $state->subst);
        if ($subst) {
            return unit(new State($subst, $state->count));
        }
        return mzero();
    };
}

function unit(State $state) {
    return new PairStream($state, mzero());
}

function mzero() {
    return new EmptyStream();
}

// really just means non-empty array
function is_pair($value) {
    return is_array($value) && count($value) > 0;
}

function unify($u, $v, Substitution $subst) {
    $u = $subst->walk($u);
    $v = $subst->walk($v);

    if (is_variable($u) && is_variable($v) && $u->is_equal($v)) {
        return $subst;
    }
    if (is_variable($u)) {
        return $subst->extend($u, $v);
    }
    if (is_variable($v)) {
        return $subst->extend($v, $u);
    }
    if (is_pair($u) && is_pair($v)) {
        $subst = unify(first($u), first($v), $subst);
        return $subst ? unify(rest($u), rest($v), $subst) : null;
    }
    if ($u === $v) {
        return $subst;
    }
    return null;
}

function call_fresh(callable $f) {
    return function (State $state) use ($f) {
        $res = $f(variable($state->count));
        return $res($state->next());
    };
}

function disj(callable $goal1, callable $goal2) {
    return function (State $state) use ($goal1, $goal2) {
        return $goal1($state)->mplus($goal2($state));
    };
}

function conj(callable $goal1, callable $goal2) {
    return function (State $state) use ($goal1, $goal2) {
        return $goal1($state)->bind($goal2);
    };
}

function cons($value, array $stream) {
    array_unshift($stream, $value);
    return $stream;
}

function first(array $stream) {
    return array_shift($stream);
}

function rest(array $stream) {
    array_shift($stream);
    return $stream;
}

interface Stream extends \IteratorAggregate {
    function mplus(Stream $stream2);
    function bind(callable $goal);
}
<commit_msg>rename argument in cons/first/rest to $list<commit_after><?php

namespace igorw\reasoned;

// microKanren implementation

class Variable {
    public $name;
    function __construct($name) {
        $this->name = $name;
    }
    function is_equal(Variable $var) {
        return $this->name === $var->name;
    }
}

function variable($name) {
    return new Variable($name);
}

function is_variable($x) {
    return $x instanceof Variable;
}

class Substitution {
    public $values;
    function __construct(array $values = []) {
        $this->values = $values;
    }
    function walk($u) {
        if (is_variable($u) && $value = $this->find($u)) {
            return $this->walk($value);
        }
        return $u;
    }
    function find(Variable $var) {
        foreach ($this->values as list($x, $value)) {
            if ($var->is_equal($x)) {
                return $value;
            }
        }
        return null;
    }
    function extend(Variable $x, $value) {
        return new Substitution(array_merge(
            [[$x, $value]],
            $this->values
        ));
    }
    function length() {
        return count($this->values);
    }
    function reify($v) {
        $v = $this->walk($v);
        if (is_variable($v)) {
            $n = reify_name($this->length());
            return $this->extend($v, $n);
        }
        if (is_pair($v)) {
            return $this->reify(first($v))
                        ->reify(rest($v));
        }
        return $this;
    }
}

class State {
    public $subst;
    public $count;
    function __construct(Substitution $subst = null, $count = 0) {
        $this->subst = $subst ?: new Substitution();
        $this->count = $count;
    }
    function next() {
        return new State($this->subst, $this->count + 1);
    }
    function reify() {
        $v = walk_star(variable(0), $this->subst);
        return walk_star($v, (new Substitution())->reify($v));
    }
}

function eq($u, $v) {
    return function (State $state) use ($u, $v) {
        $subst = unify($u, $v, $state->subst);
        if ($subst) {
            return unit(new State($subst, $state->count));
        }
        return mzero();
    };
}

function unit(State $state) {
    return new PairStream($state, mzero());
}

function mzero() {
    return new EmptyStream();
}

// really just means non-empty array
function is_pair($value) {
    return is_array($value) && count($value) > 0;
}

function unify($u, $v, Substitution $subst) {
    $u = $subst->walk($u);
    $v = $subst->walk($v);

    if (is_variable($u) && is_variable($v) && $u->is_equal($v)) {
        return $subst;
    }
    if (is_variable($u)) {
        return $subst->extend($u, $v);
    }
    if (is_variable($v)) {
        return $subst->extend($v, $u);
    }
    if (is_pair($u) && is_pair($v)) {
        $subst = unify(first($u), first($v), $subst);
        return $subst ? unify(rest($u), rest($v), $subst) : null;
    }
    if ($u === $v) {
        return $subst;
    }
    return null;
}

function call_fresh(callable $f) {
    return function (State $state) use ($f) {
        $res = $f(variable($state->count));
        return $res($state->next());
    };
}

function disj(callable $goal1, callable $goal2) {
    return function (State $state) use ($goal1, $goal2) {
        return $goal1($state)->mplus($goal2($state));
    };
}

function conj(callable $goal1, callable $goal2) {
    return function (State $state) use ($goal1, $goal2) {
        return $goal1($state)->bind($goal2);
    };
}

function cons($value, array $list) {
    array_unshift($list, $value);
    return $list;
}

function first(array $list) {
    return array_shift($list);
}

function rest(array $list) {
    array_shift($list);
    return $list;
}

interface Stream extends \IteratorAggregate {
    function mplus(Stream $stream2);
    function bind(callable $goal);
}

{'fraction_non_alphanumeric': 0.17275790565059618, 'fraction_numerical': 0.0044064282011404875, 'mean_word_length': 2.3552173913043477, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6290273', 'n_tokens_mistral': 2627, 'n_tokens_neox': 2567, 'n_words': 583}