kx.c.java Source code

Java tutorial

Introduction

Here is the source code for kx.c.java

Source

/*
 * Copyright (c) 1998-2017 Kx Systems Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package kx;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

import org.apache.commons.lang.IncompleteArgumentException;
import org.apache.commons.lang.NotImplementedException;

/**
 * Connector class for interfacing with a kdb+ process. This class is essentially a serializer/deserializer of java types
 * to/from the kdb+ ipc wire format, enabling remote method invocation in kdb+ via tcp/ip.
 * <p>
 * To begin with, a connection may be established to a listening kdb+ process via the constructor
 * 
 * <code>c connection=new c("localhost",5000);</code>
 * </p>
 * <p>
 *  There are then 3 methods available for interacting with the connection:
 *   <ol>
 *     <li>Sending a sync message using k() <br>
 *       <code>Object result=connection.k("functionName",args);</code>
 * 
 *     <li>Sending an async message using ks()<br>
 *       <code>connection.ks("functionName",args); </code>
 *     
 *     <li>Awaiting an incoming async message using k()<br>
 *       <code>Object object=connection.k();</code>. <br> 
 *       When the connection is no longer required, it may be closed via connection.close();
 *   </ol>
 */
public class c {
    /**
     * Encoding specifies the character encoding to use when [de]-serializing strings.
     */
    private static String encoding = "ISO-8859-1";

    /** Stream for printing kdb+ objects. Defaults to System.out */
    private static PrintStream out = System.out;
    /**
     *  {@code sync}  tracks how many response messages the remote is expecting
     */
    private int sync = 0;

    /**
     * Sets character encoding for serialising/deserialising strings.
     * 
     * @param encoding The name of a supported
     *                 <a href="../lang/package-summary.html#charenc">
     *                 character encoding</a>
     * @throws UnsupportedEncodingException
     *                If the named encoding is not supported
     */
    public static void setEncoding(String encoding) throws UnsupportedEncodingException {
        c.encoding = encoding;
        out = new PrintStream(System.out, true, encoding);
    }

    /**
     * {@code s} is the socket used to communicate with the remote kdb+ process.
     */
    public Socket s;
    /**
     * {@code i} is the {@code DataInputStream} of the socket used to read data from the remote kdb+ process.
     */
    DataInputStream i;
    /**
     * {@code o} is the outputStream of the socket used to write data to the remote kdb+ process.
     */
    OutputStream o;
    /**
     * {@code b} is the buffer used to store the incoming message bytes from the remote prior to de-serialization
     */
    byte[] b;
    /**
     * {@code j} is the current position of the de-serializer within the read buffer b
     */
    int j;
    /**
     * {@code B} is the buffer used to store the outgoing message bytes when serializing an object
     */
    byte[] B;
    /**
     * {@code J} is the current position the serializer within the write buffer B
     */
    int J;
    /**
    * {@code vt} indicates the ipc version to encode with
    */
    int vt;
    /**
     * Marks whether the message being deserialized was encoded little or big endian.
     */
    boolean a;
    /**
     * Indicates whether the current connection is to a local interface. Tested when considering whether to compress an outgoing message.
     */
    boolean l;
    /**
     * Indicates whether messages should be candidates for compressing before sending.
     */
    boolean zip;

    /**
     * Sets whether or not to consider compression on outgoing messages.
     * 
     * @param b true if to use a compression. Default is false.
     * 
     * @see <a href="https://code.kx.com/q/ref/ipc/#compression">IPC compression</a>
     */
    public void zip(boolean b) {
        zip = b;
    }

    /**
     * Prepare socket for kdb+ ipc comms
     * @param x socket to setup
     * @throws IOException an I/O error occurs.
     */
    void io(Socket x) throws IOException {
        s = x;
        s.setTcpNoDelay(true);
        InetAddress addr = s.getInetAddress();
        l = addr.isAnyLocalAddress() || addr.isLoopbackAddress();
        i = new DataInputStream(s.getInputStream());
        o = s.getOutputStream();
        s.setKeepAlive(true);
    }

    /** 
     * Closes the current connection to the remote process. 
     * 
     * @throws IOException if an I/O error occurs when closing this socket.
     */
    public void close() throws IOException {
        if (null != s) {
            s.close();
            s = null;
        }
        if (null != i) {
            i.close();
            i = null;
        }
        if (null != o) {
            o.close();
            o = null;
        }
    }

    /** {@code IAuthenticate} describes interface to authenticate incoming connection based on authentication string */
    public interface IAuthenticate {
        /**
         * Checks authentication string provided to allow/reject connection. 
         * @see <a href="https://code.kx.com/q/ref/dotz/#zpw-validate-user">.z.pw</a>
         * 
         * @param s String containing username:password for authentication
         * 
         * @return true if credentials accepted. 
         */
        public boolean authenticate(String s);
    }

    /**
     * Accepts and authenticates incoming connections using kdb+ protocol.
     * 
     * @param s {@link ServerSocket} to accept connections on using kdb+ IPC protocol.
     * @param a {@link IAuthenticate} instance to authenticate incoming connections. 
     *          Accepts all incoming connections if {@code null}.
     * 
     * @throws IOException if access is denied or an I/O error occurs.
     * 
     */
    public c(ServerSocket s, kx.c.IAuthenticate a) throws IOException {
        io(s.accept());
        int n = i.read(b = new byte[99]);
        if (a != null && !a.authenticate(new String(b, 0, n > 1 ? n - 2 : 0))) {
            close();
            throw new IOException("access");
        }
        vt = n > 1 ? b[n - 2] : 0;
        b[0] = (byte) (vt < '\3' ? vt : '\3');
        o.write(b, 0, 1);
    }

    /** 
     * c#c(ServerSocket, IAuthenticate) without authentication. 
     * 
     * @param s {@link ServerSocket} to accept connections on using kdb+ IPC protocol.
     * 
     * @throws IOException an I/O error occurs.
     */
    public c(ServerSocket s) throws IOException {
        this(s, null);
    }

    /**
     * Initializes a new {@link c} instance.
     * 
     * @param host Host of remote q process
     * @param port Port of remote q process
     * @param usernamepassword Username and password as "username:password" for remote authorization
     * 
     * @throws KException if access denied
     * @throws IOException if an I/O error occurs.
     */
    public c(String host, int port, String usernamepassword) throws KException, IOException {
        this(host, port, usernamepassword, false);
    }

    /**
     * Initializes a new {@link c} instance.
     * 
     * @param host Host of remote q process
     * @param port Port of remote q process
     * @param usernamepassword Username and password as "username:password" for remote authorization
     * @param useTLS whether to use TLS to encrypt the connection
     * 
     * @throws KException if access denied
     * @throws IOException if an I/O error occurs.
     */
    public c(String host, int port, String usernamepassword, boolean useTLS) throws KException, IOException {
        B = new byte[2 + ns(usernamepassword)];
        s = new Socket(host, port);
        if (useTLS) {
            s = ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(s, host, port, true);
            ((SSLSocket) s).startHandshake();
        }
        io(s);
        J = 0;
        w(usernamepassword + "\3");
        o.write(B);
        if (1 != i.read(B, 0, 1)) {
            close();
            if (useTLS) {
                throw new KException("access");
            }
            B = new byte[1 + ns(usernamepassword)];
            io(new Socket(host, port));
            J = 0;
            w(usernamepassword);
            o.write(B);
            if (1 != i.read(B, 0, 1)) {
                close();
                throw new KException("access");
            }
        }
        vt = Math.min(B[0], 3);
    }

    /**
     * Initializes a new {@link c} instance.
     * 
     * @param host Host of remote q process
     * @param port Port of remote q process
     * 
     * @throws KException if access denied
     * @throws IOException if an I/O error occurs.
     */
    public c(String host, int port) throws KException, IOException {
        this(host, port, System.getProperty("user.name"));
    }

    /** Initializes a new {@link c} instance for the purposes of serialization only.  */
    public c() {
        vt = '\3';
        l = false;
        i = new DataInputStream(new InputStream() {
            @Override
            public int read() throws IOException {
                throw new UnsupportedOperationException("nyi");
            }
        });
        o = new OutputStream() {
            @Override
            public void write(int b) throws IOException {
                throw new UnsupportedOperationException("nyi");
            }
        };
    }

    /** {@code Month} represents kdb+ month type. */
    public static class Month implements Comparable<Month> {
        /** Number of months since Jan 2000 */
        public int i;

        public Month(int x) {
            i = x;
        }

        @Override
        public String toString() {
            int m = i + 24000, y = m / 12;
            return i == ni ? "" : i2(y / 100) + i2(y % 100) + "-" + i2(1 + m % 12);
        }

        @Override
        public boolean equals(final Object o) {
            return (o instanceof Month) ? ((Month) o).i == i : false;
        }

        @Override
        public int hashCode() {
            return i;
        }

        @Override
        public int compareTo(Month m) {
            return i - m.i;
        }
    }

    /** {@code Minute} represents kdb+ minute type. */
    public static class Minute implements Comparable<Minute> {
        /** Number of minutes passed. */
        public int i;

        public Minute(int x) {
            i = x;
        }

        @Override
        public String toString() {
            return i == ni ? "" : i2(i / 60) + ":" + i2(i % 60);
        }

        @Override
        public boolean equals(final Object o) {
            return (o instanceof Minute) ? ((Minute) o).i == i : false;
        }

        @Override
        public int hashCode() {
            return i;
        }

        @Override
        public int compareTo(Minute m) {
            return i - m.i;
        }
    }

    /** {@code Second} represents kdb+ second type. */
    public static class Second implements Comparable<Second> {
        /** Number of seconds passed. */
        public int i;

        public Second(int x) {
            i = x;
        }

        @Override
        public String toString() {
            return i == ni ? "" : new Minute(i / 60).toString() + ':' + i2(i % 60);
        }

        @Override
        public boolean equals(final Object o) {
            return (o instanceof Second) ? ((Second) o).i == i : false;
        }

        @Override
        public int hashCode() {
            return i;
        }

        @Override
        public int compareTo(Second s) {
            return i - s.i;
        }
    }

    /** {@code Timespan} represents kdb+ timestamp type. */
    public static class Timespan implements Comparable<Timespan> {
        /** Number of nanoseconds passed. */
        public long j;

        public Timespan(long x) {
            j = x;
        }

        /** Constructs {@code Timespan} using time since midnight and default timezone. */
        public Timespan() {
            this(TimeZone.getDefault());
        }

        /** 
         * Constructs {@code Timespan} using time since midnight and default timezone. 
         * 
         * @param tz {@code TimeZone} to use for deriving midnight.
         */
        public Timespan(TimeZone tz) {
            Calendar c = Calendar.getInstance(tz);
            long now = c.getTimeInMillis();
            c.set(Calendar.HOUR_OF_DAY, 0);
            c.set(Calendar.MINUTE, 0);
            c.set(Calendar.SECOND, 0);
            c.set(Calendar.MILLISECOND, 0);
            j = (now - c.getTimeInMillis()) * 1000000L;
        }

        @Override
        public String toString() {
            if (j == nj)
                return "";
            String s = j < 0 ? "-" : "";
            long jj = j < 0 ? -j : j;
            int d = ((int) (jj / 86400000000000L));
            if (d != 0)
                s += d + "D";
            return s + i2((int) ((jj % 86400000000000L) / 3600000000000L)) + ":"
                    + i2((int) ((jj % 3600000000000L) / 60000000000L)) + ":"
                    + i2((int) ((jj % 60000000000L) / 1000000000L)) + "." + i9((int) (jj % 1000000000L));
        }

        @Override
        public int compareTo(Timespan t) {
            return j > t.j ? 1 : j < t.j ? -1 : 0;
        }

        @Override
        public boolean equals(final Object o) {
            return (o instanceof Timespan) ? ((Timespan) o).j == j : false;
        }

        @Override
        public int hashCode() {
            return (int) (j ^ (j >>> 32));
        }
    }

    /**
     * {@code Dict} represents the kdb+ dictionary type.
     */
    public static class Dict {
        /** Dict keys */
        public Object x;
        /** Dict values */
        public Object y;

        public Dict(Object X, Object Y) {
            x = X;
            y = Y;
        }

        // added by rs
        public Map<Object, Object> toMap() {
            Map<Object, Object> map = new LinkedHashMap<Object, Object>();
            Object[] keys = (Object[]) x;
            Object[] values = autoboxArray(y);

            for (int i = 0; i < keys.length; i++) {
                map.put(keys[i], values[i]);
            }
            return map;
        }
    }

    /**
     * {@code Flip} represents a kdb+ table.
     */
    public static class Flip {
        /** Array of column names. */
        public String[] x;
        /** Array of arrays of the column values. */
        public Object[] y;

        public Flip(Dict X) {
            x = (String[]) X.x;
            y = (Object[]) X.y;
        }

        public Object at(String s) {
            return y[find(x, s)];
        }

        // added by rs
        public String toString() {
            // return x + ": " + y;
            // return toTable().toString();
            // return Dumper.d
            StringWriter sw = new StringWriter();
            try {
                Dumper.dump(this, sw);
                return sw.toString();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        public Table toTable() {
            return new Table(this);
        }

        public Table toTable(int rows) {
            return new Table(this, rows);
        }

        public Table toTable(int rows, int cols) {
            return new Table(this, rows, cols);
        }

    }

    // added by rs
    // TODO -- add Map slice(int row)  slice :: int -> Map
    public static class Table {
        private static final int MAX = 20;
        public String[] fields;
        public Object[][] data;
        public Map<String, Object[]> columns;

        public Table(Flip flip) {
            this.fields = flip.x;
            this.data = new Object[flip.y.length][];
            this.columns = new LinkedHashMap<String, Object[]>(fields.length);
            for (int i = 0; i < fields.length; i++) {
                this.data[i] = autoboxArray(flip.y[i]);
                columns.put(fields[i], data[i]);
            }
        }

        public Table(Flip flip, int rows) {
            this.fields = Arrays.copyOf(flip.x, rows);
            throw new NotImplementedException("Table(Flip flip, int rows)");
        }

        public Table(Flip flip, int rows, int cols) {
            this.fields = Arrays.copyOf(flip.x, rows);
            throw new NotImplementedException("Table(Flip flip, int rows)");
        }

        @Deprecated
        public String toString() {
            StringBuilder sb = new StringBuilder();
            for (String f : fields) {
                sb.append(f).append(" ");
            }
            sb.append("\n");

            int max = (data[0].length > MAX) ? MAX : data[0].length;
            for (int r = 0; r < max; r++) {
                for (String f : fields) {
                    Object[] col = columns.get(f);
                    sb.append(toString(col[r])).append(" ");
                }
                sb.append("\n");
            }

            if (max < data[0].length) {
                sb.append("...");
            }
            return sb.toString();
        }

        private String toString(Object o) {
            StringBuilder sb = new StringBuilder();

            if (o instanceof char[]) {
                return new String((char[]) o);
            } else if (o.getClass().isArray()) {
                Object[] oa = c.autoboxArray(o);
                for (Object p : oa) {
                    sb.append(toString(p)).append(" ");
                }
            } else {
                sb.append(o);
            }

            return sb.toString();
        }
    }

    // added by rs
    // TODO make it recursive
    public static Object[] autoboxArray(Object data) {
        if (data.getClass().isArray()) {
            Object[] dstArray = new Object[Array.getLength(data)];
            for (int row = 0; row < Array.getLength(data); row++) {
                Object o = Array.get(data, row);
                if (o == null) {
                    o = "::";
                }
                if (o instanceof char[]) {
                    // char arrays are strings...
                    o = new String((char[]) o);
                } else if (o.getClass().isArray()) {
                    o = autoboxArray(o);
                }
                Array.set(dstArray, row, o);

            }
            return dstArray;
        }
        throw new IncompleteArgumentException("data was not an array");
    }

    /**
     * {@code KException} is used to indicate there was an error generated by the remote process during the processing of a sync message or if the connection failed due to access credentials. 
     * Network errors are reported as IOException.
     */
    public static class KException extends Exception {
        KException(String s) {
            super(s);
        }
    }

    private void compress() {
        byte i = 0;
        boolean g;
        int j = J, f = 0, h0 = 0, h = 0;
        byte[] y = B;
        B = new byte[y.length / 2];
        int c = 12, d = c, e = B.length, p = 0, q, r, s0 = 0, s = 8, t = J, a[] = new int[256];
        System.arraycopy(y, 0, B, 0, 4);
        B[2] = 1;
        J = 8;
        w(j);
        for (; s < t; i *= 2) {
            if (0 == i) {
                if (d > e - 17) {
                    J = j;
                    B = y;
                    return;
                }
                i = 1;
                B[c] = (byte) f;
                c = d++;
                f = 0;
            }
            g = (s > t - 3) || (0 == (p = a[h = 0xFF & (y[s] ^ y[s + 1])])) || (0 != (y[s] ^ y[p]));
            if (0 < s0) {
                a[h0] = s0;
                s0 = 0;
            }
            if (g) {
                h0 = h;
                s0 = s;
                B[d++] = y[s++];
            } else {
                a[h] = s;
                f |= i;
                p += 2;
                r = s += 2;
                q = Math.min(s + 255, t);
                for (; y[p] == y[s] && ++s < q;)
                    ++p;
                B[d++] = (byte) h;
                B[d++] = (byte) (s - r);
            }
        }
        B[c] = (byte) f;
        J = 4;
        w(d);
        J = d;
    }

    private void uncompress() {
        int n = 0, r = 0, f = 0, s = 8, p = s;
        short i = 0;
        byte[] dst = new byte[ri()];
        int d = j;
        int[] aa = new int[256];
        while (s < dst.length) {
            if (i == 0) {
                f = 0xff & (int) b[d++];
                i = 1;
            }
            if ((f & i) != 0) {
                r = aa[0xff & (int) b[d++]];
                dst[s++] = dst[r++];
                dst[s++] = dst[r++];
                n = 0xff & (int) b[d++];
                for (int m = 0; m < n; m++)
                    dst[s + m] = dst[r + m];
            } else
                dst[s++] = b[d++];
            while (p < s - 1)
                aa[(0xff & (int) dst[p]) ^ (0xff & (int) dst[p + 1])] = p++;
            if ((f & i) != 0)
                p = s += n;
            i *= 2;
            if (i == 256)
                i = 0;
        }
        b = dst;
        j = 8;
    }

    void w(byte x) {
        B[J++] = x;
    }

    /** null integer, i.e. 0Ni */
    static int ni = Integer.MIN_VALUE;
    /** null long, i.e. 0N */
    static long nj = Long.MIN_VALUE;
    /** null float, i.e. 0Nf or 0n */
    static double nf = Double.NaN;

    boolean rb() {
        return 1 == b[j++];
    }

    void w(boolean x) {
        w((byte) (x ? 1 : 0));
    }

    char rc() {
        return (char) (b[j++] & 0xff);
    }

    void w(char c) {
        w((byte) c);
    }

    short rh() {
        int x = b[j++], y = b[j++];
        return (short) (a ? x & 0xff | y << 8 : x << 8 | y & 0xff);
    }

    void w(short h) {
        w((byte) (h >> 8));
        w((byte) h);
    }

    int ri() {
        int x = rh(), y = rh();
        return a ? x & 0xffff | y << 16 : x << 16 | y & 0xffff;
    }

    void w(int i) {
        w((short) (i >> 16));
        w((short) i);
    }

    UUID rg() {
        boolean oa = a;
        a = false;
        UUID g = new UUID(rj(), rj());
        a = oa;
        return g;
    }

    void w(UUID uuid) {
        if (vt < 3)
            throw new RuntimeException("Guid not valid pre kdb+3.0");
        w(uuid.getMostSignificantBits());
        w(uuid.getLeastSignificantBits());
    }

    long rj() {
        int x = ri(), y = ri();
        return a ? x & 0xffffffffL | (long) y << 32 : (long) x << 32 | y & 0xffffffffL;
    }

    void w(long j) {
        w((int) (j >> 32));
        w((int) j);
    }

    float re() {
        return Float.intBitsToFloat(ri());
    }

    void w(float e) {
        w(Float.floatToIntBits(e));
    }

    double rf() {
        return Double.longBitsToDouble(rj());
    }

    void w(double f) {
        w(Double.doubleToLongBits(f));
    }

    Month rm() {
        return new Month(ri());
    }

    void w(Month m) {
        w(m.i);
    }

    Minute ru() {
        return new Minute(ri());
    }

    void w(Minute u) {
        w(u.i);
    }

    Second rv() {
        return new Second(ri());
    }

    void w(Second v) {
        w(v.i);
    }

    Timespan rn() {
        return new Timespan(rj());
    }

    void w(Timespan n) {
        if (vt < 1)
            throw new RuntimeException("Timespan not valid pre kdb+2.6");
        w(n.j);
    }

    /** {@code Timezone} to use for temporal types serialisation. */
    public TimeZone tz = TimeZone.getDefault();
    static long k = 86400000L * 10957, n = 1000000000L;

    long o(long x) {
        return tz.getOffset(x);
    }

    long lg(long x) {
        return x + o(x);
    }

    long gl(long x) {
        return x - o(x - o(x));
    }

    Date rd() {
        int i = ri();
        return new Date(i == ni ? nj : gl(k + 86400000L * i));
    }

    void w(Date d) {
        long j = d.getTime();
        w(j == nj ? ni : (int) (lg(j) / 86400000 - 10957));
    }

    Time rt() {
        int i = ri();
        return new Time(i == ni ? nj : gl(i));
    }

    void w(Time t) {
        long j = t.getTime();
        w(j == nj ? ni : (int) (lg(j) % 86400000));
    }

    java.util.Date rz() {
        double f = rf();
        return new java.util.Date(Double.isNaN(f) ? nj : gl(k + Math.round(8.64e7 * f)));
    }

    void w(java.util.Date z) {
        long j = z.getTime();
        w(j == nj ? nf : (lg(j) - k) / 8.64e7);
    }

    Timestamp rp() {
        long j = rj(), d = j < 0 ? (j + 1) / n - 1 : j / n;
        Timestamp p = new Timestamp(j == nj ? j : gl(k + 1000 * d));
        if (j != nj)
            p.setNanos((int) (j - n * d));
        return p;
    }

    void w(Timestamp p) {
        long j = p.getTime();
        if (vt < 1)
            throw new RuntimeException("Timestamp not valid pre kdb+2.6");
        w(j == nj ? j : 1000000 * (lg(j) - k) + p.getNanos() % 1000000);
    }

    String rs() throws UnsupportedEncodingException {
        int i = j;
        for (; b[j++] != 0;)
            ;
        return (i == j - 1) ? "" : new String(b, i, j - 1 - i, encoding);
    }

    void w(String s) throws UnsupportedEncodingException {
        int i = 0, n;
        if (s != null) {
            n = ns(s);
            byte[] b = s.getBytes(encoding);
            for (; i < n;)
                w(b[i++]);
        }
        B[J++] = 0;
    }

    /** 
     * Deserializes the contents of the incoming message buffer {@code b}. 
     * 
     * @return deserialised object
     * @throws UnsupportedEncodingException If the named charset is not supported
     */
    Object r() throws UnsupportedEncodingException {
        int i = 0, n, t = b[j++];
        if (t < 0)
            switch (t) {
            case -1:
                return rb();
            case (-2):
                return rg();
            case -4:
                return b[j++];
            case -5:
                return rh();
            case -6:
                return ri();
            case -7:
                return rj();
            case -8:
                return re();
            case -9:
                return rf();
            case -10:
                return rc();
            case -11:
                return rs();
            case -12:
                return rp();
            case -13:
                return rm();
            case -14:
                return rd();
            case -15:
                return rz();
            case -16:
                return rn();
            case -17:
                return ru();
            case -18:
                return rv();
            case -19:
                return rt();
            }
        if (t > 99) {
            if (t == 100) {
                rs();
                return r();
            }
            if (t < 104)
                return b[j++] == 0 && t == 101 ? null : "func";
            if (t > 105)
                r();
            else
                for (n = ri(); i < n; i++)
                    r();
            return "func";
        }
        if (t == 99)
            return new Dict(r(), r());
        j++;
        if (t == 98)
            return new Flip((Dict) r());
        n = ri();
        switch (t) {
        case 0:
            Object[] L = new Object[n];
            for (; i < n; i++)
                L[i] = r();
            return L;
        case 1:
            boolean[] B = new boolean[n];
            for (; i < n; i++)
                B[i] = rb();
            return B;
        case 2: {
            UUID[] G = new UUID[n];
            for (; i < n; i++)
                G[i] = rg();
            return G;
        }
        case 4:
            byte[] G = new byte[n];
            for (; i < n; i++)
                G[i] = b[j++];
            return G;
        case 5:
            short[] H = new short[n];
            for (; i < n; i++)
                H[i] = rh();
            return H;
        case 6:
            int[] I = new int[n];
            for (; i < n; i++)
                I[i] = ri();
            return I;
        case 7:
            long[] J = new long[n];
            for (; i < n; i++)
                J[i] = rj();
            return J;
        case 8:
            float[] E = new float[n];
            for (; i < n; i++)
                E[i] = re();
            return E;
        case 9:
            double[] F = new double[n];
            for (; i < n; i++)
                F[i] = rf();
            return F;
        case 10:
            char[] C = new String(b, j, n, encoding).toCharArray();
            j += n;
            return C;
        case 11:
            String[] S = new String[n];
            for (; i < n; i++)
                S[i] = rs();
            return S;
        case 12:
            Timestamp[] P = new Timestamp[n];
            for (; i < n; i++)
                P[i] = rp();
            return P;
        case 13:
            Month[] M = new Month[n];
            for (; i < n; i++)
                M[i] = rm();
            return M;
        case 14:
            Date[] D = new Date[n];
            for (; i < n; i++)
                D[i] = rd();
            return D;
        case 15:
            java.util.Date[] Z = new java.util.Date[n];
            for (; i < n; i++)
                Z[i] = rz();
            return Z;
        case 16:
            Timespan[] N = new Timespan[n];
            for (; i < n; i++)
                N[i] = rn();
            return N;
        case 17:
            Minute[] U = new Minute[n];
            for (; i < n; i++)
                U[i] = ru();
            return U;
        case 18:
            Second[] V = new Second[n];
            for (; i < n; i++)
                V[i] = rv();
            return V;
        case 19:
            Time[] T = new Time[n];
            for (; i < n; i++)
                T[i] = rt();
            return T;
        }
        return null;
    }

    //object.getClass().isArray()   t(int[]) is .5 isarray is .1 lookup .05
    /**
     *  Gets the numeric type of the supplied object used in kdb+.
     * 
     * @param x Object to get the numeric type of
     * @return kdb+ type number for an object
     */
    public static int t(Object x) {
        return x instanceof Boolean ? -1
                : x instanceof UUID ? -2
                        : x instanceof Byte ? -4
                                : x instanceof Short ? -5
                                        : x instanceof Integer ? -6
                                                : x instanceof Long ? -7
                                                        : x instanceof Float ? -8
                                                                : x instanceof Double ? -9
                                                                        : x instanceof Character ? -10
                                                                                : x instanceof String ? -11
                                                                                        : x instanceof Date ? -14
                                                                                                : x instanceof Time
                                                                                                        ? -19
                                                                                                        : x instanceof Timestamp
                                                                                                                ? -12
                                                                                                                : x instanceof java.util.Date
                                                                                                                        ? -15
                                                                                                                        : x instanceof Timespan
                                                                                                                                ? -16
                                                                                                                                : x instanceof Month
                                                                                                                                        ? -13
                                                                                                                                        : x instanceof Minute
                                                                                                                                                ? -17
                                                                                                                                                : x instanceof Second
                                                                                                                                                        ? -18
                                                                                                                                                        : x instanceof boolean[]
                                                                                                                                                                ? 1
                                                                                                                                                                : x instanceof UUID[]
                                                                                                                                                                        ? 2
                                                                                                                                                                        : x instanceof byte[]
                                                                                                                                                                                ? 4
                                                                                                                                                                                : x instanceof short[]
                                                                                                                                                                                        ? 5
                                                                                                                                                                                        : x instanceof int[]
                                                                                                                                                                                                ? 6
                                                                                                                                                                                                : x instanceof long[]
                                                                                                                                                                                                        ? 7
                                                                                                                                                                                                        : x instanceof float[]
                                                                                                                                                                                                                ? 8
                                                                                                                                                                                                                : x instanceof double[]
                                                                                                                                                                                                                        ? 9
                                                                                                                                                                                                                        : x instanceof char[]
                                                                                                                                                                                                                                ? 10
                                                                                                                                                                                                                                : x instanceof String[]
                                                                                                                                                                                                                                        ? 11
                                                                                                                                                                                                                                        : x instanceof Date[]
                                                                                                                                                                                                                                                ? 14
                                                                                                                                                                                                                                                : x instanceof Time[]
                                                                                                                                                                                                                                                        ? 19
                                                                                                                                                                                                                                                        : x instanceof Timestamp[]
                                                                                                                                                                                                                                                                ? 12
                                                                                                                                                                                                                                                                : x instanceof java.util.Date[]
                                                                                                                                                                                                                                                                        ? 15
                                                                                                                                                                                                                                                                        : x instanceof Timespan[]
                                                                                                                                                                                                                                                                                ? 16
                                                                                                                                                                                                                                                                                : x instanceof Month[]
                                                                                                                                                                                                                                                                                        ? 13
                                                                                                                                                                                                                                                                                        : x instanceof Minute[]
                                                                                                                                                                                                                                                                                                ? 17
                                                                                                                                                                                                                                                                                                : x instanceof Second[]
                                                                                                                                                                                                                                                                                                        ? 18
                                                                                                                                                                                                                                                                                                        : x instanceof Flip
                                                                                                                                                                                                                                                                                                                ? 98
                                                                                                                                                                                                                                                                                                                : x instanceof Dict
                                                                                                                                                                                                                                                                                                                        ? 99
                                                                                                                                                                                                                                                                                                                        : 0;
    }

    /**
     * "number of bytes from type." A helper for nx, to assist in calculating the number of bytes required to serialize a
     * particular type.
     */
    static int[] nt = { 0, 1, 16, 0, 1, 2, 4, 8, 4, 8, 1, 0, 8, 4, 4, 8, 8, 4, 4, 4 };

    /**
     * A helper function for nx, calculates the number of bytes which would be required to serialize the supplied string.
     * 
     * @param s String to be serialized
     * @return number of bytes required to serialise a string
     * 
     * @throws UnsupportedEncodingException  If the named charset is not supported
     */
    static int ns(String s) throws UnsupportedEncodingException {
        int i;
        if (s == null)
            return 0;
        if (-1 < (i = s.indexOf('\000')))
            s = s.substring(0, i);
        return s.getBytes(encoding).length;
    }

    /**
     * A helper function for nx, returns the number of elements in the supplied object.
     * e.g. for a Dict, the number of keys
     *      for a Flip, the number of rows
     *      an array, the length of the array
     * 
     * @param x Object to be serialized
     * 
     * @return number of elements in an object.
     * 
     * @throws UnsupportedEncodingException  If the named charset is not supported
     */
    public static int n(Object x) throws UnsupportedEncodingException {
        return x instanceof Dict ? n(((Dict) x).x)
                : x instanceof Flip ? n(((Flip) x).y[0])
                        : x instanceof char[] ? new String((char[]) x).getBytes(encoding).length
                                : Array.getLength(x);
    }

    /**
     * Calculates the number of bytes which would be required to serialize the supplied object.
     * 
     * @param x Object to be serialized
     * 
     * @return number of bytes required to serialise an object.
     * 
     * @throws UnsupportedEncodingException  If the named charset is not supported
     */
    public int nx(Object x) throws UnsupportedEncodingException {
        int i = 0, n, t = t(x), j;
        if (t == 99)
            return 1 + nx(((Dict) x).x) + nx(((Dict) x).y);
        if (t == 98)
            return 3 + nx(((Flip) x).x) + nx(((Flip) x).y);
        if (t < 0)
            return t == -11 ? 2 + ns((String) x) : 1 + nt[-t];
        j = 6;
        n = n(x);
        if (t == 0 || t == 11)
            for (; i < n; ++i)
                j += t == 0 ? nx(((Object[]) x)[i]) : 1 + ns(((String[]) x)[i]);
        else
            j += n * nt[t];
        return j;
    }

    void w(Object x) throws UnsupportedEncodingException {
        int i = 0, n, t = t(x);
        w((byte) t);
        if (t < 0)
            switch (t) {
            case -1:
                w(((Boolean) x).booleanValue());
                return;
            case -2:
                w((UUID) x);
                return;
            case -4:
                w(((Byte) x).byteValue());
                return;
            case -5:
                w(((Short) x).shortValue());
                return;
            case -6:
                w(((Integer) x).intValue());
                return;
            case -7:
                w(((Long) x).longValue());
                return;
            case -8:
                w(((Float) x).floatValue());
                return;
            case -9:
                w(((Double) x).doubleValue());
                return;
            case -10:
                w(((Character) x).charValue());
                return;
            case -11:
                w((String) x);
                return;
            case -12:
                w((Timestamp) x);
                return;
            case -13:
                w((Month) x);
                return;
            case -14:
                w((Date) x);
                return;
            case -15:
                w((java.util.Date) x);
                return;
            case -16:
                w((Timespan) x);
                return;
            case -17:
                w((Minute) x);
                return;
            case -18:
                w((Second) x);
                return;
            case -19:
                w((Time) x);
                return;
            }
        if (t == 99) {
            Dict r = (Dict) x;
            w(r.x);
            w(r.y);
            return;
        }
        B[J++] = 0;
        if (t == 98) {
            Flip r = (Flip) x;
            B[J++] = 99;
            w(r.x);
            w(r.y);
            return;
        }
        w(n = n(x));
        if (t == 10) {
            byte[] b = new String((char[]) x).getBytes(encoding);
            for (; i < b.length;)
                w(b[i++]);
        } else
            for (; i < n; ++i)
                if (t == 0)
                    w(((Object[]) x)[i]);
                else if (t == 1)
                    w(((boolean[]) x)[i]);
                else if (t == 2)
                    w(((UUID[]) x)[i]);
                else if (t == 4)
                    w(((byte[]) x)[i]);
                else if (t == 5)
                    w(((short[]) x)[i]);
                else if (t == 6)
                    w(((int[]) x)[i]);
                else if (t == 7)
                    w(((long[]) x)[i]);
                else if (t == 8)
                    w(((float[]) x)[i]);
                else if (t == 9)
                    w(((double[]) x)[i]);
                else if (t == 11)
                    w(((String[]) x)[i]);
                else if (t == 12)
                    w(((Timestamp[]) x)[i]);
                else if (t == 13)
                    w(((Month[]) x)[i]);
                else if (t == 14)
                    w(((Date[]) x)[i]);
                else if (t == 15)
                    w(((java.util.Date[]) x)[i]);
                else if (t == 16)
                    w(((Timespan[]) x)[i]);
                else if (t == 17)
                    w(((Minute[]) x)[i]);
                else if (t == 18)
                    w(((Second[]) x)[i]);
                else
                    w(((Time[]) x)[i]);
    }

    /**
     * Serialises {@code x} object as {@code byte[]} array.
     * 
     * @param msgType type of the ipc message
     * @param x object to serialise
     * @param zip true if to attempt compress serialised output
     * @return {@code B} containing serialised representation
     * 
     * @throws IOException should not throw
     */
    public byte[] serialize(int msgType, Object x, boolean zip) throws IOException {
        int length = 8 + nx(x);
        synchronized (o) {
            B = new byte[length];
            B[0] = 0;
            B[1] = (byte) msgType;
            J = 4;
            w(length);
            w(x);
            if (zip && J > 2000 && !l)
                compress();
            return B;
        }
    }

    /**
     * Deserialises {@code buffer} q ipc as an object
     * 
     * @param buffer byte[] to deserialise object from 
     * @return deserialised object
     * 
     * @throws KException if buffer contains kdb+ error object.
     * @throws UnsupportedEncodingException  If the named charset is not supported
     */
    public Object deserialize(byte[] buffer) throws KException, UnsupportedEncodingException {
        synchronized (i) {
            b = buffer;
            a = b[0] == 1; // endianness of the msg 
            boolean compressed = b[2] == 1;
            j = 8;
            if (compressed)
                uncompress();
            if (b[8] == -128) {
                j = 9;
                throw new KException(rs());
            }
            return r(); // deserialize the message
        }
    }

    protected void w(int msgType, Object x) throws IOException {
        synchronized (o) {
            byte[] buffer = serialize(msgType, x, zip);
            o.write(buffer, 0, buffer.length);
        }
    }

    /**
     * Sends a response message to the remote kdb+ process. This should be called only during processing of an incoming sync message.
     * 
     * @param obj Object to send to the remote
     * 
     * @throws IOException if not expecting any response
     */
    public void kr(Object obj) throws IOException {
        if (sync == 0)
            throw new IOException("Unexpected response msg");
        sync--;
        w(2, obj);
    }

    /**
     * Sends an error as a response message to the remote kdb+ process. This should be called only during processing of an incoming sync message.
     * 
     * @param text The error message text
     * 
     * @throws IOException unexpected error message
     */
    public void ke(String text) throws IOException {
        if (sync == 0)
            throw new IOException("Unexpected error msg");
        sync--;
        int n = 2 + ns(text) + 8;
        synchronized (o) {
            B = new byte[n];
            B[0] = 0;
            B[1] = 2;
            J = 4;
            w(n);
            w((byte) -128);
            w(text);
            o.write(B);
        }
    }

    /**
     * Sends an async message to the remote kdb+ process. This blocks until the serialized data has been written to the
     * socket. On return, there is no guarantee that this msg has already been processed by the remote process.
     * 
     * @param expr The expression to send
     * 
     * @throws IOException if an I/O error occurs.
     */
    public void ks(String expr) throws IOException {
        w(0, cs(expr));
    }

    /**
     * Sends an async message to the remote kdb+ process. This blocks until the serialized data has been written to the
     * socket. On return, there is no guarantee that this msg has already been processed by the remote process.
     * 
     * @param obj The object to send
     * 
     * @throws IOException if an I/O error occurs.
     */
    public void ks(Object obj) throws IOException {
        w(0, obj);
    }

    char[] cs(String s) {
        return s.toCharArray();
    }

    /**
     * Sends an async message to the remote kdb+ process. This blocks until the serialized data has been written to the
     * socket. On return, there is no guarantee that this msg has already been processed by the remote process. Use this to
     * invoke a function in kdb+ which takes a single argument and does not return a value. e.g. to invoke f[x] use
     * ks("f",x); to invoke a lambda, use ks("{x}",x);
     * 
     * @param s The name of the function, or a lambda itself
     * @param x The argument to the function named in s
     * 
     * @throws IOException if an I/O error occurs.
     */
    public void ks(String s, Object x) throws IOException {
        Object[] a = { cs(s), x };
        w(0, a);
    }

    /**
     * Sends an async message to the remote kdb+ process. This blocks until the serialized data has been written to the
     * socket. On return, there is no guarantee that this msg has already been processed by the remote process. Use this to
     * invoke a function in kdb+ which takes 2 arguments and does not return a value. e.g. to invoke f[x;y] use ks("f",x,y);
     * to invoke a lambda, use ks("{x+y}",x,y);
     * 
     * @param s The name of the function, or a lambda itself
     * @param x The first argument to the function named in s
     * @param y The second argument to the function named in s
     * 
     * @throws IOException if an I/O error occurs.
     */
    public void ks(String s, Object x, Object y) throws IOException {
        Object[] a = { cs(s), x, y };
        w(0, a);
    }

    /**
     * Sends an async message to the remote kdb+ process. This blocks until the serialized data has been written to the
     * socket. On return, there is no guarantee that this msg has already been processed by the remote process. Use this to
     * invoke a function in kdb+ which takes 3 arguments and does not return a value. e.g. to invoke f[x;y;z] use
     * ks("f",x,y,z); to invoke a lambda, use ks("{x+y+z}",x,y,z);
     * 
     * @param s The name of the function, or a lambda itself
     * @param x The first argument to the function named in s
     * @param y The second argument to the function named in s
     * @param z The third argument to the function named in s
     * 
     * @throws IOException if an I/O error occurs.
     */
    public void ks(String s, Object x, Object y, Object z) throws IOException {
        Object[] a = { cs(s), x, y, z };
        w(0, a);
    }

    /**
     * Sends an async message to the remote kdb+ process. This blocks until the serialized data has been written to the
     * socket. On return, there is no guarantee that this msg has already been processed by the remote process. Use this to
     * invoke a function in kdb+ which takes a variable number of arguments and does not return a value. e.g. to invoke f[x;y;z] use
     * ks("f",x,y,z); to invoke a lambda, use ks("{x+y+z}",x,y,z);
     * 
     * @param s The name of the function, or a lambda itself
     * @param args Variable length arguements to the function named in s. Note, q supports a maximum of 8 arguments
     * 
     * @throws IOException if an I/O error occurs.
     */
    public void ks(String s, Object... args) throws IOException {
        Object[] a = new Object[args.length + 1];
        a[0] = cs(s);
        System.arraycopy(args, 0, a, 1, args.length);
        w(0, a);
    }

    /**
     * Reads an incoming message from the remote kdb+ process. This blocks until a single message has been received and
     * deserialized. This is called automatically during a sync request via k(String s,..). It can be called explicitly when
     * subscribing to a publisher.
     * 
     * @return deserialised object
     * 
     * @throws KException if response contains an error
     * @throws IOException if an I/O error occurs.
     * @throws UnsupportedEncodingException If the named charset is not supported
     */
    public Object k() throws KException, IOException, UnsupportedEncodingException {
        synchronized (i) {
            i.readFully(b = new byte[8]); // read the msg header
            a = b[0] == 1; // endianness of the msg 
            if (b[1] == 1) // msg types are 0 - async, 1 - sync, 2 - response
                sync++; // an incoming sync message means the remote will expect a response message
            j = 4;
            b = Arrays.copyOf(b, ri());
            i.readFully(b, 8, b.length - 8); // read the incoming message in full
            return deserialize(b);
        }
    }

    /**
     * Sends a sync message to the remote kdb+ process. This blocks until the message has been sent in full, and a message
     * is received from the remote; typically the received message would be the corresponding response message.
     * 
     * @param x The object to send
     * @return deserialised response to request {@code x}
     * 
     * @throws KException if request evaluation resulted in an error
     * @throws IOException if an I/O error occurs.
     */
    public synchronized Object k(Object x) throws KException, IOException {
        w(1, x);
        return k();
    }

    /**
     * Sends a sync message to the remote kdb+ process. This blocks until the message has been sent in full, and a message
     * is received from the remote; typically the received message would be the corresponding response message.
     * 
     * @param expr The expression to send
     * @return deserialised response to request {@code x}
     * 
     * @throws KException if request evaluation resulted in an error
     * @throws IOException if an I/O error occurs.
     */
    public Object k(String expr) throws KException, IOException {
        return k(cs(expr));
    }

    /**
     * Sends a sync message to the remote kdb+ process. This blocks until the message has been sent in full, and a message
     * is received from the remote; typically the received message would be the corresponding response message. Use this to
     * invoke a function in kdb+ which takes a single argument and returns a value. e.g. to invoke f[x] use k("f",x); to
     * invoke a lambda, use k("{x}",x);
     * 
     * @param s The name of the function, or a lambda itself
     * @param x The argument to the function named in s   
     * @return deserialised response to request {@code s} with params {@code x}
     * 
     * @throws KException if request evaluation resulted in an error
     * @throws IOException if an I/O error occurs.
     */
    public Object k(String s, Object x) throws KException, IOException {
        Object[] a = { cs(s), x };
        return k(a);
    }

    /**
     * Sends a sync message to the remote kdb+ process. This blocks until the message has been sent in full, and a message
     * is received from the remote; typically the received message would be the corresponding response message. Use this to
     * invoke a function in kdb+ which takes arguments and returns a value. e.g. to invoke f[x;y] use k("f",x,y); to invoke
     * a lambda, use k("{x+y}",x,y);
     * 
     * @param s The name of the function, or a lambda itself
     * @param x The first argument to the function named in s
     * @param y The second argument to the function named in s
     * @return deserialised response to the request
     * 
     * @throws KException if request evaluation resulted in an error
     * @throws IOException if an I/O error occurs.
     */
    public Object k(String s, Object x, Object y) throws KException, IOException {
        Object[] a = { cs(s), x, y };
        return k(a);
    }

    /**
     * Sends a sync message to the remote kdb+ process. This blocks until the message has been sent in full, and a message
     * is received from the remote; typically the received message would be the corresponding response message. Use this to
     * invoke a function in kdb+ which takes 3 arguments and returns a value. e.g. to invoke f[x;y;z] use k("f",x,y,z); to
     * invoke a lambda, use k("{x+y+z}",x,y,z);
     * 
     * @param s The name of the function, or a lambda itself
     * @param x The first argument to the function named in s
     * @param y The second argument to the function named in s
     * @param z The third argument to the function named in s
     * @return deserialised response to the request
     * 
     * @throws KException if request evaluation resulted in an error
     * @throws IOException if an I/O error occurs.
     */
    public Object k(String s, Object x, Object y, Object z) throws KException, IOException {
        Object[] a = { cs(s), x, y, z };
        return k(a);
    }

    /**
     * Sends a sync message to the remote kdb+ process. This blocks until the message has been sent in full, and a message
     * is received from the remote; typically the received message would be the corresponding response message. Use this to
     * invoke a function in kdb+ which takes a variable number of arguments and returns a value. e.g. to invoke f[x;y;z] use k("f",x,y,z); to
     * invoke a lambda, use k("{x+y+z}",x,y,z);
     * 
     * @param s The name of the function, or a lambda itself
     * @param args Variable length arguements to the function named in s. Note, q supports a maximum of 8 arguments
     * @return deserialised response to the request
     * 
     * @throws KException if request evaluation resulted in an error
     * @throws IOException if an I/O error occurs.
     */
    public Object k(String s, Object x, Object y, Object z, Object... rest) throws KException, IOException {
        Object[] a = new Object[rest.length + 4];
        a[0] = cs(s);
        a[1] = x;
        a[2] = y;
        a[3] = z;
        System.arraycopy(rest, 0, a, 4, rest.length);
        return k(a);
    }

    /** Array containing null object for corresponing kdb+ type number(0-19). For example {@code "".equals(NULL[11])} */
    public static Object[] NULL = { null, new Boolean(false), new UUID(0, 0), null, new Byte((byte) 0),
            new Short(Short.MIN_VALUE), new Integer(ni), new Long(nj), new Float(nf), new Double(nf),
            new Character(' '), "", new Timestamp(nj), new Month(ni), new Date(nj), new java.util.Date(nj),
            new Timespan(nj), new Minute(ni), new Second(ni), new Time(nj) };

    /**
     * Gets a null object for the type indicated by the character.
     * 
     * @param c The shorthand character for the type
     * 
     * @return instance of null object of specified kdb+ type.
     */
    public static Object NULL(char c) {
        return NULL[" bg xhijefcspmdznuvt".indexOf(c)];
    }

    /**
     * Tests whether an object is a null object of that type.
     * qn(NULL('j')) should return true
     * 
     * @param x The object to be tested for null
     * 
     * @return true if {@code x} is kdb+ null, false otherwise
     */
    public static boolean qn(Object x) {
        int t = -t(x);
        return (t == 2 || t > 4) && x.equals(NULL[t]);
    }

    /**
     * Gets the object at an index of an array
     * 
     * @param x The array to index
     * @param i The offset to index at
     * @return object at index
     */
    public static Object at(Object x, int i) {
        return qn(x = Array.get(x, i)) ? null : x;
    }

    /**
     * Sets the object at an index of an array
     * 
     * @param x The array to index
     * @param i The offset to index at
     * @param y The object to set at index i
     */
    public static void set(Object x, int i, Object y) {
        Array.set(x, i, null == y ? NULL[t(x)] : y);
    }

    static int find(String[] x, String y) {
        int i = 0;
        for (; i < x.length && !x[i].equals(y);)
            ++i;
        return i;
    }

    /**
     * Removes the key from a keyed table. 
     * <p>
     * A keyed table(a.k.a. Flip) is a dictionary where both key and value are tables
     * themselves. For ease of processing, this method, td, table from dictionary, can be used to remove the key.
     * </p>
     * @param X A table or keyed table.
     * @return A simple table
     * 
     * @throws UnsupportedEncodingException If the named charset is not supported
     */
    public static Flip td(Object X) throws UnsupportedEncodingException {
        if (X instanceof Flip)
            return (Flip) X;
        Dict d = (Dict) X;
        Flip a = (Flip) d.x, b = (Flip) d.y;
        int m = n(a.x), n = n(b.x);
        String[] x = new String[m + n];
        System.arraycopy(a.x, 0, x, 0, m);
        System.arraycopy(b.x, 0, x, m, n);
        Object[] y = new Object[m + n];
        System.arraycopy(a.y, 0, y, 0, m);
        System.arraycopy(b.y, 0, y, m, n);
        return new Flip(new Dict(x, y));
    }

    /** Prints x to {@code out} stream 
     * @param x object to print
     * @return object that has been printed
     */
    public static Object O(Object x) {
        out.println(x);
        return x;
    }

    /** 
     * Prints x to {@code out} stream 
     * @param x value to print
     */
    public static void O(int x) {
        out.println(x);
    }

    /** 
     * Prints x to {@code out} stream 
     * @param x value to print
     */
    public static void O(boolean x) {
        out.println(x);
    }

    /** 
     * Prints x to {@code out} stream 
     * @param x value to print
     */
    public static void O(long x) {
        out.println(x);
    }

    /** 
     *Prints x to {@code out} stream
     * @param x value to print
     */
    public static void O(double x) {
        out.println(x);
    }

    /** 
     * Current time in milliseconds 
     * @return current time in millis.
     */
    public static long t() {
        return System.currentTimeMillis();
    }

    static long t;

    public static void tm() {
        long u = t;
        t = t();
        if (u > 0)
            O(t - u);
    }

    static String i2(int i) {
        return new DecimalFormat("00").format(i);
    }

    static String i9(int i) {
        return new DecimalFormat("000000000").format(i);
    }
}