package nova;

import java.io.*;
import java.math.*;
import java.net.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.nio.file.*;
import java.util.*;

/**
 * Warranty & Liability
 * To the extent permitted by applicable law and unless explicitly
 * otherwise agreed upon, XLOG Technologies AG makes no warranties
 * regarding the provided information. XLOG Technologies AG assumes
 * no liability that any problems might be solved with the information
 * provided by XLOG Technologies AG.
 *
 * Rights & License
 * All industrial property rights regarding the information - copyright
 * and patent rights in particular - are the sole property of XLOG
 * Technologies AG. If the company was not the originator of some
 * excerpts, XLOG Technologies AG has at least obtained the right to
 * reproduce, change and translate the information.
 *
 * Reproduction is restricted to the whole unaltered document. Reproduction
 * of the information is only allowed for non-commercial uses. Selling,
 * giving away or letting of the execution of the library is prohibited.
 * The library can be distributed as part of your applications and libraries
 * for execution provided this comment remains unchanged.
 *
 * Restrictions
 * Only to be distributed with programs that add significant and primary
 * functionality to the library. Not to be distributed with additional
 * software intended to replace any components of the library.
 *
 * Trademarks
 * Jekejeke is a registered trademark of XLOG Technologies AG.
 */
public final class runtime {
    private static final int MAX_BUF = 4096;

    public static String codebase = System.getProperty("user.dir") + File.separatorChar;
    private static String bootbase = "";

    /**
     * Set the boot base.
     *
     * @param url The boot base.
     */
    public static void set_bootbase(String url) {
        bootbase = url;
    }

    /**
     * <p>Resolve a child path against a parent.</p>
     *
     * @param parent The parent path.
     * @param child  The child path.
     * @return The result.
     */
    public static File findWrite(String parent, String child) {
        File res = new File(child);
        if (!res.isAbsolute())
            res = new File(parent, child);
        return res;
    }

    /**************************************************************/
    /* current_output/1 and current_error/1                       */
    /**************************************************************/

    public static final int MASK_DST_COLR = 0x00000004;
    public static final int MASK_DST_CYCL = 0x00000008;
    public static final int MASK_DST_FEAT = MASK_DST_COLR | MASK_DST_CYCL;

    public static final class Sink {
        public StringBuilder buf;
        public Handler.Sender send;
        public int last;
        public int flags;
        public Handler.Notifier notify;
        public Handler.Releaser release;
        public Object data;
        public int indent;

        /**
         * Create a text output.
         */
        public Sink() {
            this.buf = new StringBuilder();
            this.send = (fd, buf) -> fd;
            this.last = -1;
            this.flags = 0;
            this.notify = (fd) -> { };
            this.release = (fd) -> { };
            this.data = Store.UNDEF_OBJ;
            this.indent = 0;
        }

    }

    /**
     * current_output(S): [ISO 8.11.2]
     * The built-in succeeds in S with the current output.
     */
    private static boolean test_current_output(Object[] args) {
        Object alpha = Store.engine.text_output;
        return Machine.exec_unify(args[0], alpha);
    }

    /**
     * current_error(S):
     * The built-in succeeds in S with the current error.
     */
    private static boolean test_current_error(Object[] args) {
        Object alpha = Store.engine.text_error;
        return Machine.exec_unify(args[0], alpha);
    }

    /**************************************************************/
    /* set_output/1 and set_error/1                               */
    /**************************************************************/

    /**
     * set_output(S): [ISO 8.11.4]
     * The built-in succeeds. As a side effect the current output is set to S.
     */
    private static boolean test_set_output(Object[] args) {
        Object obj = Machine.exec_build(args[0]);
        check_sink(obj);
        Store.engine.text_output = (Sink) obj;
        return true;
    }

    /**
     * set_error(S):
     * The built-in succeeds. As a side effect the current error is set to S.
     */
    private static boolean test_set_error(Object[] args) {
        Object obj = Machine.exec_build(args[0]);
        check_sink(obj);
        Store.engine.text_error = (Sink) obj;
        return true;
    }

    /**
     * Assure that the object is a sink.
     *
     * @param beta The object.
     */
    public static void check_sink(Object beta) {
        if (!(beta instanceof Sink)) {
            Machine.check_nonvar(beta);
            beta = Store.copy_term(beta);
            throw Machine.make_error(new Store.Compound("type_error",
                    new Object[]{"writer", beta}));
        }
    }

    /**************************************************************/
    /* put_atom/2                                                 */
    /**************************************************************/

    /**
     * put_atom(S, A):
     * The built-in succeeds. As a side effect, it adds
     * the atom to the stream S.
     */
    private static boolean test_put_atom(Object[] args) {
        Object stream = Machine.exec_build(args[0]);
        check_sink(stream);
        Object text = Machine.exec_build(args[1]);
        special.check_atom(text);
        put_atom((Sink) stream, (String) text);
        return true;
    }

    public static void put_atom(Sink stream, String text) {
        if (text.length() > 0) {
            stream.last = text.codePointBefore(text.length());
            if (stream.buf != null) {
                stream.buf.append(text);
                if (stream.buf.length() >= MAX_BUF)
                    flush_buffer(stream);
            } else {
                stream.data = stream.send.eval(stream.data, text);
            }
        }
    }

    private static void flush_buffer(Sink stream) {
        if (stream.buf != null && stream.buf.length() > 0) {
            String text = stream.buf.toString();
            stream.buf = new StringBuilder();
            stream.data = stream.send.eval(stream.data, text);
        }
    }

    /**************************************************************/
    /* current_input/1 and set_input/1                            */
    /**************************************************************/

    public static final int MASK_SRC_SKIP = 0x00000001;
    public static final int MASK_SRC_AREAD = 0x00000002;

    public static final class Source {
        public String buf;
        public int pos;
        public Object receive;
        public int flags;
        public int lineno;
        public Object release;
        public Object data;

        /**
         * Create a text input.
         */
        public Source() {
            this.buf = "";
            this.pos = 0;
            this.receive = (Handler.Receiver) ((fd) -> "");
            this.flags = 0;
            this.lineno = 0;
            this.release = (Handler.Releaser) ((fd) -> {});
            this.data = Store.UNDEF_OBJ;
        }

    }

    /**
     * current_input(S): [ISO 8.11.1]
     * The built-in succeeds in S with the current input.
     */
    private static boolean test_current_input(Object[] args) {
        Object alpha = Store.engine.text_input;
        return Machine.exec_unify(args[0], alpha);
    }

    /**
     * set_input(S): [ISO 8.11.3]
     * The built-in succeeds. As a side effect it sets the current input to S.
     */
    private static boolean test_set_input(Object[] args) {
        Object obj = Machine.exec_build(args[0]);
        check_source(obj);
        Store.engine.text_input = (Source) obj;
        return true;
    }

    /**
     * Assure that the object is a source.
     *
     * @param beta The object.
     */
    private static void check_source(Object beta) {
        if (!(beta instanceof Source)) {
            Machine.check_nonvar(beta);
            beta = Store.copy_term(beta);
            throw Machine.make_error(new Store.Compound("type_error",
                    new Object[]{"reader", beta}));
        }
    }

    /*******************************************************************/
    /* os_get_code/2 and os_peek_code/2                                */
    /*******************************************************************/

    /**
     * os_get_code(S, C):
     * The predicate succeeds in C with the Unicode point from the stream buffer S.
     * As a side effect the stream position is advanced.
     */
    private static boolean test_os_get_code(Object[] args) {
        Source stream = (Source) Machine.exec_build(args[0]);
        int pos = stream.pos;
        String buf = stream.buf;
        if (pos < buf.length()) {
            int ch = buf.codePointAt(pos);
            pos += Character.charCount(ch);
            if (ch == 13 || (ch == 10 && (stream.flags & MASK_SRC_SKIP) == 0))
                stream.lineno++;
            if (ch == 13) {
                stream.flags |= MASK_SRC_SKIP;
            } else {
                stream.flags &= ~MASK_SRC_SKIP;
            }
            stream.pos = pos;
            return Machine.exec_unify(args[1], Integer.valueOf(ch));
        } else {
            return false;
        }
    }

    /**
     * os_peek_code(S, C):
     * The built-in succeeds in C with the Unicode point from the stream buffer S.
     */
    private static boolean test_os_peek_code(Object[] args) {
        Source stream = (Source) Machine.exec_build(args[0]);
        int pos = stream.pos;
        String buf = stream.buf;
        if (pos < buf.length()) {
            int ch = buf.codePointAt(pos);
            return Machine.exec_unify(args[1], Integer.valueOf(ch));
        } else {
            return false;
        }
    }

    /****************************************************************/
    /* os_open_promise_opts/4                                       */
    /****************************************************************/

    /**
     * os_open_promise_opts(P, L, S, Q):
     * The predicate succeeds in Q with a promise for open input S
     * on path P and option list L.
     */
    private static boolean test_os_open_promise_opts(Object[] args) {
        Object obj = Machine.exec_build(args[0]);
        special.check_atom(obj);
        String url = (String) obj;
        Map opts = (Map) Machine.exec_build(args[1]);
        Source stream = new Source();
        if (!Machine.exec_unify(args[2], stream))
            return false;
        Object buf = Machine.ctx;
        Handler.Promise prom = open_file_promise_opts(buf, stream, url, opts);
        return Machine.exec_unify(args[3], prom);
    }

    public static Charset get_encoding(Map opts) {
        if (opts != null && opts.get("encoding") != null) {
            String encoding = opts.get("encoding").toString();
            return Charset.forName(encoding);
        } else {
            return StandardCharsets.UTF_8;
        }
    }

    private static Handler.Promise open_file_promise_opts(Object buf,
                Source stream, String url, Map opts) {
        return new Handler.Promise(() -> {
            File file = findWrite(codebase, url);
            try {
                InputStream ins = new FileInputStream(file);
                stream.data = new InputStreamReader(ins, get_encoding(opts));
                stream.receive = (Handler.Factory) runtime::file_read_promise;
                stream.release = (Handler.Factory) runtime::file_close_promise;
                stream.flags |= MASK_SRC_AREAD;
            } catch (IOException err) {
                Machine.register_signal(buf, map_file_error(err, url));
            }
        });
    }

    public static Object map_file_error(Exception err, String url) {
        if (err instanceof InterruptedException) {
            return new Store.Compound("resource_error",
                    new Object[]{"interrupted_exception"});
        } else if (err instanceof FileNotFoundException ||
              err instanceof NoSuchFileException) {
            return new Store.Compound("existence_error",
                    new Object[]{"source_sink", url});
        } else if (err instanceof ConnectException) {
            Throwable cause = err.getCause();
            if (cause != null && cause.getCause() instanceof UnresolvedAddressException) {
                return new Store.Compound("resource_error", new Object[]{"unknown_host"});
            } else {
                return new Store.Compound("resource_error", new Object[]{"connect_failed"});
            }
        } else {
            return new Store.Compound("resource_error", new Object[]{"remote_error"});
        }
    }

    /*******************************************************************/
    /* os_read_promise/2 and os_close_promise/2                        */
    /*******************************************************************/

    /**
     * os_read_promise(S, P):
     * The predicate suceeds in P with a read promise for a input S.
     */
    private static boolean test_os_read_promise(Object[] args) {
        Source stream = (Source) Machine.exec_build(args[0]);
        Object buf = Machine.ctx;
        return Machine.exec_unify(args[1], ((Handler.Factory) stream.receive).eval(buf, stream));
    }

    public static Handler.Promise file_read_promise(Object buf, Source stream) {
        return new Handler.Promise(() -> {
            try {
                char[] cbuf = new char[8192];
                int len = ((Reader)stream.data).read(cbuf, 0, cbuf.length);
                stream.buf = (len > 0 ? new String(cbuf, 0, len) : "");
                stream.pos = 0;
            } catch (IOException err) {
                Machine.register_signal(buf, map_stream_error(err));
            }
        });
    }

    /**
     * Map a stream error when in transit.
     *
     * @param err The offending error.
     * @return The Prolog error term.
     */
    public static Object map_stream_error(Exception err) {
        if (err instanceof SocketTimeoutException) {
            return new Store.Compound("resource_error",
                    new Object[]{"socket_timeout"});
        } else if (err instanceof BindException) {
            return new Store.Compound("resource_error",
                    new Object[]{"port_error"});
        } else if (err instanceof SocketException) {
            return new Store.Compound("resource_error",
                    new Object[]{"remote_error"});
        } else {
            return new Store.Compound("resource_error",
                    new Object[]{"io_exception"});
        }
    }

    /**
     * os_close_promise(S, P):
     * The predicate suceeds in P with a read promise for a input S.
     */
    private static boolean test_os_close_promise(Object[] args) {
        Source stream = (Source) Machine.exec_build(args[0]);
        Object buf = Machine.ctx;
        return Machine.exec_unify(args[1], ((Handler.Factory) stream.release).eval(buf, stream));
    }

    public static Handler.Promise file_close_promise(Object buf, Source stream) {
        return new Handler.Promise(() -> {
            try {
                ((Closeable) stream.data).close();
            } catch (IOException err) {
                Machine.register_signal(buf, map_stream_error(err));
            }
        });
    }

    /*******************************************************************/
    /* os_prop_promise/3                                               */
    /*******************************************************************/

    /**
     * os_prop_promise(F, M, Q):
     * The predicate succeeds in Q with with a promise for the
     * properties M of the file F. Barks if path F doesn't exist
     * or io exception while resolving.
     */
    private static boolean test_os_prop_promise(Object[] args) {
        Object obj = Machine.exec_build(args[0]);
        special.check_atom(obj);
        String url = (String) obj;
        HashMap res = new HashMap();
        if (!Machine.exec_unify(args[1], res))
            return false;
        Object buf = Machine.ctx;
        Handler.Promise prom = prop_file_promise(buf, url, res);
        return Machine.exec_unify(args[2], prom);
    }

    private static Handler.Promise prop_file_promise(Object buf, String url, Map res) {
        return new Handler.Promise(() -> {
            File file = findWrite(codebase, url);
            try {
                if (!file.exists())
                    throw new NoSuchFileException("");

                String ftype = (file.isFile() ? "regular" : (file.isDirectory() ? "directory" : "other"));

                res.put("last_modified", special.norm_smallint(file.lastModified()));
                res.put("absolute_path", file.getPath());
                res.put("type", ftype);
                res.put("can_write", (file.canWrite() ? "true" : "false"));
            } catch (IOException err) {
                Machine.register_signal(buf, map_file_error(err, url));
            }
        });
    }

    /**************************************************************/
    /* os_stream_flags/2 and os_open_sync_opts/4                  */
    /**************************************************************/

    /**
     * os_stream_flags(S, F):
     * The predicate succeeds in F with the flags of the stream S.
     */
    private static boolean test_os_stream_flags(Object[] args) {
        Object obj = Machine.exec_build(args[0]);
        int flags;
        if (obj instanceof Source) {
            flags = ((Source) obj).flags;
        } else {
            check_sink(obj);
            flags = ((Sink) obj).flags;
        }
        return Machine.exec_unify(args[1], Integer.valueOf(flags));
    }

    /**
     * os_open_sync_opts(P, M, L, S):
     * The predicate succeeds. As a side effect the stream S is
     * opened on the path P with the mode M and the option list L.
     */
    private static boolean test_os_open_sync_opts(Object[] args) {
        Object obj = Machine.exec_build(args[0]);
        special.check_atom(obj);
        String url = (String) obj;
        Object mode = Machine.exec_build(args[1]);
        special.check_atom(mode);
        Map opts = (Map) Machine.exec_build(args[2]);
        Object stream;
        if ("read".equals(mode)) {
            throw Machine.make_error(new Store.Compound("resource_error",
                    new Object[]{"not_implemented"}));
        } else if ("write".equals(mode)) {
            stream = open_write(url, false, opts);
        } else if ("append".equals(mode)) {
            stream = open_write(url, true, opts);
        } else {
            throw Machine.make_error(new Store.Compound("domain_error",
                    new Object[]{"io_mode", mode}));
        }
        return Machine.exec_unify(args[3], stream);
    }

    public static Object open_write(String url, boolean append, Map opts) {
        try {
            File file = findWrite(codebase, url);
            OutputStream outs = new FileOutputStream(file, append);
            Sink dst = new Sink();
            dst.data = new OutputStreamWriter(outs, get_encoding(opts));
            dst.send = runtime::file_write;
            dst.release = runtime::file_close;
            dst.notify = runtime::file_flush;
            return dst;
        } catch (IOException err) {
            throw Machine.make_error(map_file_error(err, url));
        }
    }

    public static Object file_write(Object data, String buf) {
        try {
            int len = buf.length();
            int pos = 0;
            char[] cbuf = null;
            while (len > 0) {
                int span = Math.min(len, 8192);
                if (cbuf == null)
                    cbuf = new char[span];
                buf.getChars(pos, span, cbuf, 0);
                ((Writer) data).write(cbuf, 0, span);
                pos += span;
                len -= span;
            }
            return data;
        } catch (IOException err) {
            throw Machine.make_error(map_stream_error(err));
        }
    }

    public static void file_flush(Object data) {
        try {
            ((Flushable) data).flush();
        } catch (IOException err) {
            throw Machine.make_error(map_stream_error(err));
        }
    }

    public static void file_close(Object data) {
        try {
            ((Closeable) data).close();
        } catch (IOException err) {
            throw Machine.make_error(map_stream_error(err));
        }
    }

    /**************************************************************/
    /* flush_output/1 and os_close_sync/1                         */
    /**************************************************************/

    /**
     * flush_output(S): [ISO 8.11.7]
     * The built-in succeeds. As a side effect, it flushes the stream S buffer.
     */
    private static boolean test_flush_output(Object[] args) {
        Object stream = Machine.exec_build(args[0]);
        check_sink(stream);
        flush(stream);
        return true;
    }

    public static void flush(Object obj) {
        Sink stream = (Sink)obj;
        flush_buffer(stream);
        stream.notify.run(stream.data);
    }

    /**
     * close(S): [ISO 8.11.6]
     * The built-in succeeds. As a side effect, the stream S is closed.
     */
    private static boolean test_os_close_sync(Object[] args) {
        Object stream = Machine.exec_build(args[0]);
        if (stream instanceof Sink) {
            /* */
        } else {
            check_source(stream);
        }
        close(stream);
        return true;
    }

    public static void close(Object stream) {
        if (stream instanceof Sink) {
            flush_buffer((Sink) stream);
            ((Sink) stream).release.run(((Sink) stream).data);
        } else {
            ((Handler.Releaser)((Source) stream).release).run(((Source) stream).data);
        }
    }

    /*******************************************************************/
    /* os_cntrl_sync/2 and os_read_sync/1                              */
    /*******************************************************************/

    /**
     * os_cntrl_sync(F, P):
     * The predicate assigns the property P to the file F.
     */
    private static boolean test_os_cntrl_sync(Object[] args) {
        Object obj = Machine.exec_build(args[0]);
        special.check_atom(obj);
        String url = (String)obj;
        File file = findWrite(codebase, url);
        Object prop = Machine.exec_build(args[1]);
        if (!file.exists())
            throw Machine.make_error(new Store.Compound("existence_error",
                    new Object[]{"source_sink", url}));
        if (Store.is_structure(prop) &&
                ((Store.Structure) prop).functor.equals("last_modified") &&
                ((Store.Structure) prop).args.length == 1) {
            Object val = Store.deref(((Store.Structure) prop).args[0]);
            special.check_integer(val);
            if (eval.integer_signum((Number) val) < 0)
                throw Machine.make_error(new Store.Compound("domain_error",
                        new Object[]{"not_less_than_zero", val}));
            if (!is_long((Number) val))
                throw Machine.make_error(new Store.Compound("representation_error",
                        new Object[]{"long", val}));
            if (!file.setLastModified(((Number) val).longValue())) {
                throw Machine.make_error(new Store.Compound("resource_error",
                        new Object[]{"io_exception"}));
            }
        } else if (Store.is_structure(prop) &&
                ((Store.Structure) prop).functor.equals("can_write") &&
                ((Store.Structure) prop).args.length == 1) {
            Object val = Store.deref(((Store.Structure) prop).args[0]);
            special.check_atom(val);
            if (!"true".equals(val) && !"false".equals(val))
                throw Machine.make_error(new Store.Compound("domain_error",
                        new Object[]{"boolean", val}));
            if (!file.setWritable("true".equals(val))) {
                throw Machine.make_error(new Store.Compound("resource_error",
                        new Object[]{"io_exception"}));
            }
        } else {
            Machine.check_nonvar(prop);
            prop = Store.copy_term(prop);
            throw Machine.make_error(new Store.Compound("domain_error",
                    new Object[]{"prolog_property", prop}));
        }
        return true;
    }

    private static final BigInteger MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE);
    private static final BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);

    public static boolean is_long(Number num) {
        if (num instanceof Integer) {
            return true;
        } else {
            return MIN_LONG.compareTo((BigInteger) num) <= 0 &&
                    ((BigInteger) num).compareTo(MAX_LONG) <= 0;
        }
    }

    /**
     * os_read_sync(S):
     * The predicate succeeds. As a side effect the stream buffer is read.
     */
    private static boolean test_os_read_sync(Object[] args) {
        Source stream = (Source) Machine.exec_build(args[0]);
        stream.buf = ((Handler.Receiver) stream.receive).eval(stream.data);
        stream.pos = 0;
        return true;
    }

    /**************************************************************/
    /* ir_anon_new/1, ir_place_new/2 and ir_skeleton_new/3        */
    /**************************************************************/

    /**
     * ir_anon_new(S):
     * The predicate succeeds in S with a new annonymous variable.
     */
    private static boolean test_ir_anon_new(Object[] args) {
        Object alpha = Store.UNDEF_OBJ;
        return Machine.exec_unify(args[0], new Store.Quote(alpha));
    }

    /**
     * ir_place_new(I, S):
     * The predicate succeeds in S with a new place for index I.
     */
    private static boolean test_ir_place_new(Object[] args) {
        Object alpha = Machine.exec_build(args[0]);
        special.check_integer(alpha);
        alpha = new Store.Place(((Integer) alpha).intValue());
        return Machine.exec_unify(args[1], new Store.Quote(alpha));
    }

    /**
     * ir_skeleton_new(F, L, S):
     * The predicate succeeds in S with a new
     * skeleton for functor F and list L.
     */
    private static boolean test_ir_skeleton_new(Object[] args) {
        Object alpha = Machine.exec_build(args[0]);
        Object beta = Machine.exec_build(args[1]);
        beta = special.list_objects(beta);
        Store.unquote_objects((Object[])beta);
        alpha = new Store.Skeleton(alpha, (Object[])beta);
        return Machine.exec_unify(args[2], new Store.Quote(alpha));
    }

    /**************************************************************/
    /* ir_make_frozen/2                                           */
    /**************************************************************/

    /**
     * ir_make_frozen(F, L, S):
     * The predicate succeeds in S with a new frozen
     * compound for functor F and list L.
     */
    private static boolean test_ir_make_frozen(Object[] args) {
        Object alpha = Machine.exec_build(args[0]);
        Object beta = Machine.exec_build(args[1]);
        beta = special.list_objects(beta);
        alpha = new Store.Frozen(alpha, (Object[])beta);
        return Machine.exec_unify(args[2], alpha);
    }

    /**************************************************************/
    /* ir_pred_site/2, ir_is_site/1 and ir_site_name/2            */
    /**************************************************************/

    /**
     * ir_pred_site(X, Y): internal only
     * The built-in succeeds in Y with a cacheable X.
     */
    private static boolean test_ir_pred_site(Object[] args) {
        Object alpha = Machine.exec_build(args[0]);
        if (Store.is_structure(alpha)) {
            if (Machine.is_atom(((Store.Structure) alpha).functor))
                alpha = new Store.Compound(new Store.Cache((String)
                        ((Store.Structure) alpha).functor),
                        ((Store.Structure) alpha).args);
        } else if (Machine.is_atom(alpha)) {
            alpha = new Store.Cache((String) alpha);
        }
        return Machine.exec_unify(args[1], alpha);
    }

    /**
     * ir_is_site(X): internal only
     * The built-in succeeds if X is cacheable.
     */
    private static boolean test_ir_is_site(Object[] args) {
        Object alpha = Machine.exec_build(args[0]);
        if (Store.is_structure(alpha))
            return Store.is_cache(((Store.Structure) alpha).functor);
        return Store.is_cache(alpha);
    }

    /**
     * ir_site_name(X, Y): internal only
     * The built-in succeeds in Y with a cachefree X.
     */
    private static boolean test_ir_site_name(Object[] args) {
        Object alpha = Machine.exec_build(args[0]);
        if (Store.is_structure(alpha)) {
            if (Store.is_cache(((Store.Structure) alpha).functor))
                alpha = new Store.Compound(((Store.Cache) ((Store.Structure)
                        alpha).functor).name, ((Store.Structure) alpha).args);
        } else if (Store.is_cache(alpha)) {
            alpha = ((Store.Cache) alpha).name;
        }
        return Machine.exec_unify(args[1], alpha);
    }

    /**************************************************************/
    /* ir_clause_new/6 and ir_clause_add/4                        */
    /**************************************************************/

    /**
     * ir_clause_new(S, H, B, R, D, C):
     * The built-in succeeds in C with a Java object representing
     * a clause with variable count S, head instructions H, body instructions B,
     * cut variable index R and head index option D.
     */
    private static boolean test_ir_clause_new(Object[] args) {
        Object alpha = Machine.exec_build(args[0]);
        special.check_integer(alpha);
        Object beta = Machine.exec_build(args[1]);
        Object[] head = special.list_objects(beta);
        Object gamma = Machine.exec_build(args[2]);
        Object[] body = special.list_objects(gamma);
        Object delta = Machine.exec_build(args[3]);
        special.check_integer(delta);
        Object mue = Machine.exec_build(args[4]);
        if (Store.is_structure(mue) &&
                "just".equals(((Store.Structure) mue).functor) &&
                ((Store.Structure) mue).args.length == 1) {
            mue = Store.deref(((Store.Structure) mue).args[0]);
        } else {
            mue = Store.UNDEF_OBJ;
        }
        int size = ((Integer) alpha).intValue();
        Store.unquote_objects(head);
        Store.unquote_objects(body);
        int cutvar = ((Integer) delta).intValue();
        return Machine.exec_unify(args[5], new Store.Clause(size, head, body, cutvar, mue));
    }

    /**
     * ir_clause_add(F, A, C, O): internal only
     * The built-in succeeds. As a side effect the Java object clause C
     * is added according to options O to the knowledge base for the predicate
     * indicator F/A. If C is not a clause, but directly a Java function
     * of a special, the built-in definition of the indicator F/A is updated.
     */
    private static boolean test_ir_clause_add(Object[] args) {
        Object functor = Machine.exec_build(args[0]);
        special.check_atom(functor);
        Object arity = Machine.exec_build(args[1]);
        special.check_integer(arity);
        Object gamma = Machine.exec_build(args[2]);
        Object flags = Machine.exec_build(args[3]);
        special.check_integer(flags);
        Store.add_clause((String) functor, ((Integer) arity).intValue(),
                gamma, ((Integer) flags).intValue());
        return true;
    }

    /**************************************************************/
    /* ir_goal_new/4 and ir_goal_call/1                           */
    /**************************************************************/

    /**
     * ir_goal_new(S, B, R, G):
     * The built-in succeeds in G with a Java object representing
     * a goal with variable count S, body instructions B and the
     * cut variable index R.
     */
    private static boolean test_ir_goal_new(Object[] args) {
        Object alpha = Machine.exec_build(args[0]);
        special.check_integer(alpha);
        Object beta = Machine.exec_build(args[1]);
        Object[] body = special.list_objects(beta);
        Object gamma = Machine.exec_build(args[2]);
        special.check_integer(gamma);
        int size = ((Integer) alpha).intValue();
        Store.unquote_objects(body);
        int cutvar = ((Integer) gamma).intValue();
        return Machine.exec_unify(args[3], new Store.Goal(size, body, cutvar));
    }

    /**
     * ir_goal_call(A):
     * The predicate succeeds whenever the compiled goal A succeeds.
     */
    private static Object special_ir_goal_call(Object[] args) {
        Object goal = Store.deref(args[0]);
        check_goal(goal);
        goal = Machine.melt_directive((Store.Goal) goal);
        special.solve_seq(goal);
        return Boolean.TRUE;
    }

    public static void check_goal(Object beta) {
        if (!Store.is_goal(beta)) {
            Machine.check_nonvar(beta);
            beta = Store.copy_term(beta);
            throw Machine.make_error(new Store.Compound("type_error",
                    new Object[]{"goal", beta}));
        }
    }

    /**************************************************************/
    /* kb_clause_ref/3 and kb_pred_touch/3                        */
    /**************************************************************/

    private static final int MASK_FIND_MODIFY = 0x00000001;
    private static final int MASK_FIND_DYNAMIC = 0x00000002;
    private static final int MASK_FIND_REVERSE = 0x00000004;

    /**
     * kb_clause_ref(H, F, C): internal only
     * The built-in succeeds in C with the clause references
     * for the head H and the flags F.
     */
    private static Object special_kb_clause_ref(Object[] args) {
        Object head = Store.deref(args[0]);
        Machine.check_callable(head);
        Object beta = Store.deref(args[1]);
        special.check_integer(beta);
        int flags = ((Number) beta).intValue();
        Store.Provable peek = Machine.lookup_pred(head);
        if (peek == null)
            return false;
        if ((flags & MASK_FIND_DYNAMIC) != 0)
            if ((peek.flags & Store.MASK_PRED_DYNAMIC) == 0)
                make_error_find(head, flags);
        if (!Store.is_logical(peek.rope))
            return false;
        Object[] temp;
        if (Store.is_structure(head)) {
            temp = ((Store.Structure) head).args;
        } else {
            temp = Machine.VOID_ARGS;
        }
        Store.Logical rope = Machine.defined_clauses(peek, temp);
        Store.Clause[] data = Store.snapshot_data(rope);
        if ((flags & MASK_FIND_REVERSE) == 0) {
            return solve2_ref(args, data, 0, null);
        } else {
            return solve2_ref_reverse(args, data, data.length, null);
        }
    }

    private static void make_error_find(Object head, int flags) {
        if ((flags & MASK_FIND_MODIFY) != 0) {
            throw Machine.make_error(new Store.Compound("permission_error",
                    new Object[]{"modify", "static_procedure", Machine.make_indicator_term(head)}));
        } else {
            throw Machine.make_error(new Store.Compound("permission_error",
                    new Object[]{"access", "private_procedure", Machine.make_indicator_term(head)}));
        }
    }

    private static Object solve_ref(Object rope, int at, Machine.Choice choice) {
        Object goal = Store.deref(((Store.Structure) Machine.call).args[0]);
        return solve2_ref(((Store.Structure) goal).args, rope, at, choice);
    }

    private static Object solve_ref_reverse(Object rope, int at, Machine.Choice choice) {
        Object goal = Store.deref(((Store.Structure) Machine.call).args[0]);
        return solve2_ref_reverse(((Store.Structure) goal).args, rope, at, choice);
    }

    /**
     * Search a Prolog clause and return it.
     *
     * @param args   The current arguments.
     * @param data   The clause list.
     * @param at     The clause index.
     * @param choice The choice point for reuse or null.
     * @return boolean True if search succeeds, otherwise false.
     */
    private static Object solve2_ref(Object[] args, Object data, int at, Machine.Choice choice) {
        Store.Clause[] rope = (Store.Clause[]) data;
        Store.Variable mark = Machine.trail;
        while (at < rope.length) {
            Store.Clause clause = rope[at++];
            if (Machine.unify(args[2], clause)) {
                if (at < rope.length) {
                    if (choice == null) {
                        choice = new Machine.Choice(runtime::solve_ref, rope, at, mark);
                    } else {
                        choice.at = at;
                    }
                    Machine.more(choice);
                }
                Machine.cont(((Store.Structure) Machine.call).args[1]);
                return Boolean.TRUE;
            }
            Machine.unbind(mark);
        }
        return Boolean.FALSE;
    }

    /**
     * Search a Prolog clause backwards and return it.
     *
     * @param args   The current arguments.
     * @param data   The clause list.
     * @param at     The clause index.
     * @param choice The choice point for reuse or null.
     * @return boolean True if search succeeds, otherwise false.
     */
    private static Object solve2_ref_reverse(Object[] args, Object data, int at, Machine.Choice choice) {
        Store.Clause[] rope = (Store.Clause[]) data;
        Store.Variable mark = Machine.trail;
        while (at > 0) {
            Store.Clause clause = rope[--at];
            if (Machine.unify(args[2], clause)) {
                if (at > 0) {
                    if (choice == null) {
                        choice = new Machine.Choice(runtime::solve_ref_reverse, rope, at, mark);
                    } else {
                        choice.at = at;
                    }
                    Machine.more(choice);
                }
                Machine.cont(((Store.Structure) Machine.call).args[1]);
                return Boolean.TRUE;
            }
            Machine.unbind(mark);
        }
        return Boolean.FALSE;
    }

    /**
     * kb_pred_touch(F, N, O): internal only
     * The built-in succeeds. As a side effect the predicate
     * indicator F/N with options O is touched.
     */
    private static boolean test_kb_pred_touch(Object[] args) {
        Object functor = Machine.exec_build(args[0]);
        special.check_atom(functor);
        Object beta = Machine.exec_build(args[1]);
        special.check_integer(beta);
        Object gamma = Machine.exec_build(args[2]);
        special.check_integer(gamma);
        Store.pred_touch((String) functor, ((Number) beta).intValue(),
                ((Number) gamma).intValue());
        return true;
    }

    /**************************************************************/
    /* kb_clause_remove/4 and kb_pred_destroy/2                   */
    /**************************************************************/

    /**
     * kb_clause_remove(F, N, G, R): internal only
     * The built-in succeeds if the clause or predicate R could
     * be removed from the predicate indicator F/N and the flags G.
     */
    private static boolean test_kb_clause_remove(Object[] args) {
        Object functor = Machine.exec_build(args[0]);
        special.check_atom(functor);
        Object beta = Machine.exec_build(args[1]);
        special.check_integer(beta);
        int arity = ((Number) beta).intValue();
        Object gamma = Machine.exec_build(args[2]);
        special.check_integer(gamma);
        int flags = ((Number) gamma).intValue();
        Object clause = Machine.exec_build(args[3]);
        check_clause(clause);
        return Store.remove_clause((String) functor, arity, clause, flags);
    }

    public static void check_clause(Object beta) {
        if (!Store.is_clause(beta)) {
            Machine.check_nonvar(beta);
            beta = Store.copy_term(beta);
            throw Machine.make_error(new Store.Compound("type_error",
                    new Object[]{"clause", beta}));
        }
    }

    /**
     * kb_pred_destroy(F, N): internal only
     * The built-in succeeds. As a side effect the
     * predicate indicator F/N is destroyed.
     */
    private static boolean test_kb_pred_destroy(Object[] args) {
        Object functor = Machine.exec_build(args[0]);
        special.check_atom(functor);
        Object beta = Machine.exec_build(args[1]);
        special.check_integer(beta);
        int arity = ((Number) beta).intValue();
        Store.pred_destroy((String) functor, arity);
        return true;
    }

    /**************************************************************/
    /* kb_make_defined/2 and kb_pred_link/3                       */
    /**************************************************************/

    /**
     * kb_make_defined(L, K, P): internal only
     * The built-in succeeds in P with an anonymous predicate
     * for the clauses L and the selected indexing argument K.
     */
    private static boolean test_kb_make_defined(Object[] args) {
        Object alpha = Machine.exec_build(args[0]);
        Object[] rope = special.list_objects(alpha);
        Object beta = Machine.exec_build(args[1]);
        special.check_integer(beta);
        int offset = ((Number) beta).intValue();
        return Machine.exec_unify(args[2], Store.make_defined(rope, offset));
    }

    /**
     * kb_pred_link(F, A, Q): internal only
     * The built-in succeeds in Q with the provable of the
     * predicate indicator F/A. Otherwise if no such provable
     * exists the built-in fails.
     */
    private static boolean test_kb_pred_link(Object[] args) {
        Object functor = Machine.exec_build(args[0]);
        special.check_atom(functor);
        Object beta = Machine.exec_build(args[1]);
        special.check_integer(beta);
        int arity = ((Number) beta).intValue();
        Store.Provable peek = Store.pred_link((String) functor, arity);
        if (peek == null)
            return false;
        return Machine.exec_unify(args[2], peek);
    }

    /**************************************************************/
    /* kb_is_link/1, kb_link_flags/2 and kb_pred_list/1           */
    /**************************************************************/

    /**
     * kb_is_link(Q): internal only
     * The built-in succeeds if Q is a provable.
     */
    private static boolean test_kb_is_link(Object[] args) {
        Object alpha = Machine.exec_build(args[0]);
        if (Store.is_structure(alpha))
            return Store.is_provable(((Store.Structure) alpha).functor);
        return Store.is_provable(alpha);
    }

    /**
     * kb_link_flags(Q, F): internal only
     * The built-in succeeds in F with the flags of the provable Q.
     */
    private static boolean test_kb_link_flags(Object[] args) {
        Object peek = Machine.exec_build(args[0]);
        check_provable(peek);
        int res = ((Store.Provable) peek).flags >> Store.MASK_PRED_BITS;
        return Machine.exec_unify(args[1], Integer.valueOf(res));
    }

    /**
     * Assure that the object is a provable.
     *
     * @param beta The object.
     */
    public static void check_provable(Object beta) {
        if (!Store.is_provable(beta)) {
            Machine.check_nonvar(beta);
            beta = Store.copy_term(beta);
            throw Machine.make_error(new Store.Compound("type_error",
                    new Object[]{"provable", beta}));
        }
    }

    /**
     * kb_pred_list(L): internal only
     * The built-in succeeds in L with the current predicate indicators.
     */
    private static boolean test_kb_pred_list(Object[] args) {
        Object res = kb_pred_list();
        return Machine.exec_unify(args[0], res);
    }

    private static Object kb_pred_list() {
        Store.Structure back = null;
        Object res = null;
        Iterator it = Store.kb.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            List temp = (List)entry.getValue();
            for (int i = 0; i < temp.size(); i++) {
                Store.Provable peek = (Store.Provable)temp.get(i);
                if (peek == null || peek.remover != Store.UNDEF_INT)
                    continue;
                Store.Structure elem = new Store.Compound(".",
                        new Object[]{Machine.make_indicator(entry.getKey(), i),
                                Store.UNDEF_OBJ});
                if (back == null) {
                    res = elem;
                } else {
                    back.args[1] = elem;
                }
                back = elem;
            }
        }
        if (back == null) {
            res = "[]";
        } else {
            back.args[1] = "[]";
        }
        return res;
    }

    /**************************************************************/
    /* kb_clause_shard/2 and kb_clause_arity/2                    */
    /**************************************************************/

    /**
     * kb_clause_shard(C, S): internal only
     * The built-in succeeds in S with the shard of the clause C.
     */
    private static boolean test_kb_clause_shard(Object[] args) {
        Object clause = Machine.exec_build(args[0]);
        check_clause(clause);
        return Machine.exec_unify(args[1], ((Store.Clause) clause).shard);
    }

    /**
     * kb_clause_arity(C, A): internal only
     * The built-in succeeds in A with the arity of the clause C.
     */
    private static boolean test_kb_clause_arity(Object[] args) {
        Object clause = Machine.exec_build(args[0]);
        check_clause(clause);
        int res = ((Store.Clause)clause).head.length;
        return Machine.exec_unify(args[1], Integer.valueOf(res));
    }

    /**************************************************************/
    /* kb_clause_head/2 and kb_clause_data/4                      */
    /**************************************************************/

    /**
     * kb_clause_head(C, H): internal only
     * The built-in succeeds in H with the head of the clause C.
     */
    private static Object special_kb_clause_head(Object[] args) {
        Object beta = Store.deref(args[0]);
        check_clause(beta);
        Store.Clause clause = (Store.Clause) beta;
        Object head = Store.deref(args[1]);
        Object[] paras;
        if (Store.is_structure(head)) {
            paras = ((Store.Structure) head).args;
        } else {
            paras = Machine.VOID_ARGS;
        }
        Object[] display;
        if (clause.size != 0) {
            display = new Object[clause.size];
        } else {
            display = null;
        }
        if (!Machine.exec_head(clause.head, display, paras))
            return Boolean.FALSE;
        Machine.cont(((Store.Structure) Machine.call).args[1]);
        return Boolean.TRUE;
    }

    /**
     * kb_clause_data(C, H, O, L): internal only
     * The built-in succeeds in H, O and L with the head,
     * the cut var and the compiled body of the clause C.
     */
    private static Object special_kb_clause_data(Object[] args) {
        Object beta = Store.deref(args[0]);
        check_clause(beta);
        Store.Clause clause = (Store.Clause) beta;
        Object head = Store.deref(args[1]);
        Object[] paras;
        if (Store.is_structure(head)) {
            paras = ((Store.Structure) head).args;
        } else {
            paras = Machine.VOID_ARGS;
        }
        Object[] display;
        if (clause.size != 0) {
            display = new Object[clause.size];
        } else {
            display = null;
        }
        if (!Machine.exec_head(clause.head, display, paras))
            return Boolean.FALSE;
        Object temp;
        int peek = clause.cutvar;
        if (peek != -1) {
            temp = new Store.Variable();
            display[peek] = temp;
            temp = new Store.Compound("just", new Object[]{temp});
        } else {
            temp = "nothing";
        }
        if (!Machine.unify(args[2], temp))
            return Boolean.FALSE;
        temp = Machine.exec_body(clause.body, display);
        if (!Machine.unify(args[3], temp))
            return Boolean.FALSE;
        Machine.cont(((Store.Structure) Machine.call).args[1]);
        return Boolean.TRUE;
    }

    /**************************************************************/
    /* dg_get_partition/1 and dg_set_partition/1                  */
    /**************************************************************/

    /**
     * dg_get_partition(D): internal only
     * The built-in succeeds in D with the current stage.
     */
    private static boolean test_dg_get_partition(Object[] args) {
        return Machine.exec_unify(args[0], Store.engine.partition);
    }

    /**
     * dg_set_partition(D): internal only
     * The built-in succeeds. As a side effect it changes the current partition.
     */
    private static boolean test_dg_set_partition(Object[] args) {
        Object value = Machine.exec_build(args[0]);
        special.check_atom(value);
        Store.set_partition((String) value);
        return true;
    }

    /**************************************************************/
    /* sys_stat_map/1, sys_flags_get/1 and garbage_collect/0        */
    /**************************************************************/

    /**
     * sys_stat_map(M): internal only
     * The built-in succeeds in W with the statistics map.
     */
    private static boolean test_sys_stat_map(Object[] args) {
        HashMap res = new HashMap();
        res.put("wall", special.norm_smallint(System.currentTimeMillis()));
        res.put("time", special.norm_smallint(Machine.real_time()));
        res.put("maxinfs", special.norm_smallint(Machine.GC_MAX_INFERS));
        res.put("calls", special.norm_smallint(Machine.gc_enter));
        res.put("gctime", special.norm_smallint(Machine.gc_time));
        res.put("used", special.norm_smallint(Runtime.getRuntime().totalMemory() -
                Runtime.getRuntime().freeMemory()));
        return Machine.exec_unify(args[0], res);
    }

    /**
     * sys_flags_get(W): internal only
     * The built-in succeeds in W with the garbage collector flags.
     */
    private static boolean test_dg_gc_flags(Object[] args) {
        return Machine.exec_unify(args[0], Integer.valueOf(Store.engine.flags));
    }

    /**
     * garbage_collect: internal only
     * The built-in succeeds in attempting a garbage colection.
     */
    private static boolean test_garbage_collect(Object[] args) {
        Machine.gc_major();
        Runtime.getRuntime().gc();
        return true;
    }

    /**************************************************************/
    /* sys_flag_map/1                                             */
    /**************************************************************/

    /**
     * sys_flag_map(M): internal only
     * The built-in succeeds in W with a Prolog flag map.
     */
    private static boolean test_sys_flag_map(Object[] args) {
        HashMap res = new HashMap();
        res.put("system_url", bootbase);
        res.put("foreign_ext", special.NAT_EXT);
        res.put("host_info", System.getProperty("java.vendor")+", Java "+System.getProperty("java.version"));
        res.put("mach_info", System.getProperty("os.arch")+", "+System.getProperty("os.name"));
        return Machine.exec_unify(args[0], res);
    }

    /**************************************************************/
    /* os_get_workdir/1 and os_set_workdir/1                      */
    /**************************************************************/

    /**
     * os_get_workdir(D): internal only
     * The built-in succeeds in D with the working directory.
     */
    private static boolean test_os_get_workdir(Object[] args) {
        Object url = codebase;
        return Machine.exec_unify(args[0], url);
    }

    /**
     * os_set_workdir(D): internal only
     * The built-in succeeds. As a side effect it changes the working directory to D.
     */
    private static boolean test_os_set_workdir(Object[] args) {
        Object obj = Machine.exec_build(args[0]);
        special.check_atom(obj);
        String url = (String)obj;

        File file = findWrite(codebase, url);
        try {
            if (!file.exists())
                throw new NoSuchFileException("");
            if (!file.isDirectory())
                throw new IOException("");
        } catch (IOException err) {
            throw Machine.make_error(map_file_error(err, url));
        }

        codebase = file.getPath() + File.separatorChar;
        return true;
    }

    /**************************************************************/
    /* dg_clear_stage/0, dg_get_stage/1 and dg_set_stage/1        */
    /**************************************************************/

    /**
     * dg_clear_stage: internal only
     * The built-in succeeds. As a side effect it clears the current stage.
     */
    private static boolean test_dg_clear_stage(Object[] args) {
        Store.clear();
        return true;
    }

    /**
     * dg_get_stage(D): internal only
     * The built-in succeeds in D with the current stage.
     */
    private static boolean test_dg_get_stage(Object[] args) {
        return Machine.exec_unify(args[0], Integer.valueOf(Store.stage));
    }

    /**
     * dg_set_stage(D): internal only
     * The built-in succeeds. As a side effect it changes the current stage.
     */
    private static boolean test_dg_set_stage(Object[] args) {
        Object value = Machine.exec_build(args[0]);
        special.check_integer(value);
        Store.set_stage(((Number) value).intValue());
        return true;
    }

    /**************************************************************/
    /* Runtime Init                                               */
    /**************************************************************/

    public static void boot() {
        // stream specials, output
        Store.add("current_output", 1, special.make_check(runtime::test_current_output));
        Store.add("current_error", 1, special.make_check(runtime::test_current_error));
        Store.add("set_output", 1, special.make_check(runtime::test_set_output));
        Store.add("set_error", 1, special.make_check(runtime::test_set_error));
        Store.add("put_atom", 2, special.make_check(runtime::test_put_atom));

        // stream specials, input
        Store.add("current_input", 1, special.make_check(runtime::test_current_input));
        Store.add("set_input", 1, special.make_check(runtime::test_set_input));
        Store.add("os_get_code", 2, special.make_check(runtime::test_os_get_code));
        Store.add("os_peek_code", 2, special.make_check(runtime::test_os_peek_code));

        // stream specials, async
        Store.add("os_open_promise_opts", 4, special.make_check(runtime::test_os_open_promise_opts));
        Store.add("os_read_promise", 2, special.make_check(runtime::test_os_read_promise));
        Store.add("os_close_promise", 2, special.make_check(runtime::test_os_close_promise));
        Store.add("os_prop_promise", 3, special.make_check(runtime::test_os_prop_promise));

        // stream specials, sync
        Store.add("os_stream_flags", 2, special.make_check(runtime::test_os_stream_flags));
        Store.add("os_open_sync_opts", 4, special.make_check(runtime::test_os_open_sync_opts));
        Store.add("flush_output", 1, special.make_check(runtime::test_flush_output));
        Store.add("os_close_sync", 1, special.make_check(runtime::test_os_close_sync));
        Store.add("os_cntrl_sync", 2, special.make_check(runtime::test_os_cntrl_sync));
        Store.add("os_read_sync", 1, special.make_check(runtime::test_os_read_sync));

        // intermediate representation, Albufeira code
        Store.add("ir_anon_new", 1, special.make_check(runtime::test_ir_anon_new));
        Store.add("ir_place_new", 2, special.make_check(runtime::test_ir_place_new));
        Store.add("ir_skeleton_new", 3, special.make_check(runtime::test_ir_skeleton_new));
        Store.add("ir_make_frozen", 3, special.make_check(runtime::test_ir_make_frozen));
        Store.add("ir_pred_site", 2, special.make_check(runtime::test_ir_pred_site));
        Store.add("ir_is_site", 1, special.make_check(runtime::test_ir_is_site));
        Store.add("ir_site_name", 2, special.make_check(runtime::test_ir_site_name));

        // intermediate representation specials, consult text, internal only
        Store.add("ir_clause_new", 6, special.make_check(runtime::test_ir_clause_new));
        Store.add("ir_clause_add", 4, special.make_check(runtime::test_ir_clause_add));
        Store.add("ir_goal_new", 4, special.make_check(runtime::test_ir_goal_new));
        Store.add("ir_goal_call", 1, special.make_special(runtime::special_ir_goal_call));

        // knowledge base specials, dynamic database, internal only
        Store.add("kb_clause_ref", 3, special.make_special(runtime::special_kb_clause_ref));
        Store.add("kb_pred_touch", 3, special.make_check(runtime::test_kb_pred_touch));
        Store.add("kb_clause_remove", 4, special.make_check(runtime::test_kb_clause_remove));
        Store.add("kb_pred_destroy", 2, special.make_check(runtime::test_kb_pred_destroy));

        // knowledge base specials, linked provables, internal only
        Store.add("kb_make_defined", 3, special.make_check(runtime::test_kb_make_defined));
        Store.add("kb_pred_link", 3, special.make_check(runtime::test_kb_pred_link));
        Store.add("kb_is_link", 1, special.make_check(runtime::test_kb_is_link));
        Store.add("kb_link_flags", 2, special.make_check(runtime::test_kb_link_flags));
        Store.add("kb_pred_list", 1, special.make_check(runtime::test_kb_pred_list));

        // knowledge base specials, meta data, internal only
        Store.add("kb_clause_shard", 2, special.make_check(runtime::test_kb_clause_shard));
        Store.add("kb_clause_arity", 2, special.make_check(runtime::test_kb_clause_arity));
        Store.add("kb_clause_head", 2, special.make_special(runtime::special_kb_clause_head));
        Store.add("kb_clause_data", 4, special.make_special(runtime::special_kb_clause_data));
        Store.add("dg_get_partition", 1, special.make_check(runtime::test_dg_get_partition));
        Store.add("dg_set_partition", 1, special.make_check(runtime::test_dg_set_partition));

        // system specials, flags, internal only
        Store.add("sys_stat_map", 1, special.make_check(runtime::test_sys_stat_map));
        Store.add("sys_flags_get", 1, special.make_check(runtime::test_dg_gc_flags));
        Store.add("garbage_collect", 0, special.make_check(runtime::test_garbage_collect));
        Store.add("sys_flag_map", 1, special.make_check(runtime::test_sys_flag_map));
        Store.add("os_get_workdir", 1, special.make_check(runtime::test_os_get_workdir));
        Store.add("os_set_workdir", 1, special.make_check(runtime::test_os_set_workdir));

        // system specials, staging, internal only
        Store.add("dg_clear_stage", 0, special.make_check(runtime::test_dg_clear_stage));
        Store.add("dg_get_stage", 1, special.make_check(runtime::test_dg_get_stage));
        Store.add("dg_set_stage", 1, special.make_check(runtime::test_dg_set_stage));
    }

}