T.java

package com.mackenziehigh.autumn.util;

import autumn.lang.Module;
import autumn.lang.Record;
import autumn.lang.TypedFunctor;
import autumn.lang.annotations.Infer;
import autumn.lang.exceptions.CheckedException;
import autumn.lang.internals.ArgumentStack;
import autumn.lang.internals.Helpers;
import autumn.lang.internals.Operators;
import com.mackenziehigh.autumn.resources.dev.AsyncTask;
import autumn.util.F;
import autumn.util.functors.Action;
import autumn.util.functors.Function1;
import autumn.util.functors.Function2;
import autumn.util.functors.Predicate;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mackenziehigh.autumn.util.json.JsonDecoder;
import com.mackenziehigh.autumn.util.json.JsonEncoder;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Scanner;

/**
 * This class provides commonly used static utility methods.
 *
 * <p>
 * This class has a single letter name, because it will be used very frequently.
 * As a result, the shorter name saves Autumn programmers some typing.
 * </p>
 *
 * @author Mackenzie High
 */
public final class T
{
    private static abstract class ArrayBackedList<E>
            extends AbstractList<E>
    {
    }

    private static BigInteger unique = BigInteger.ZERO;

    /**
     * Sole Constructor.
     */
    private T()
    {
        // Pass, because this is merely a static utility class.
    }

    /**
     * This method rethrows an exception quietly.
     *
     * <p>
     * If the exception is checked, then it will be wrapped in a CheckedException object
     * and then rethrown; otherwise, the exception will simply be rethrown immediately.
     * </p>
     *
     * @param problem is the exception to rethrow.
     */
    public static void rethrow(final Throwable problem)
    {
        if (problem instanceof RuntimeException)
        {
            throw (RuntimeException) problem;
        }
        else if (problem instanceof Error)
        {
            throw (Error) problem;
        }
        else
        {
            throw new CheckedException(problem);
        }
    }

    /**
     * This method applies a functor to a list of arguments.
     *
     * @param functor is the functor itself.
     * @param arguments are the arguments to apply the functor to.
     * @return the result of applying the functor to the arguments.
     * @throws NullPointerException if functor is null.
     * @throws NullPointerException if arguments is null.
     * @throws Throwable in order to propagate any exceptions thrown by the functor.
     */
    public static Object apply(final TypedFunctor functor,
                               final Iterable<?> arguments)
            throws Throwable
    {
        final ArgumentStack stack = ArgumentStack.getThreadStack();

        // Clear the stack in order to prevent unexpected bugs.
        stack.clear();

        // Push the arguments onto the stack.
        for (Object argument : arguments)
        {
            stack.push(argument);
        }

        // The number of arguments must match the number of parameters.
        if (functor.parameterTypes().size() != stack.size())
        {
            stack.clear();
            throw new IllegalArgumentException("arguments.length != parameters.length");
        }

        // Invoke the functor.
        functor.apply(stack);

        // Retrieve the return-value of the functor.
        final Object result = stack.popResult();

        return result;
    }

    /**
     * This method prints a formated string to standard-output.
     *
     * <p>
     * This is simply a convenience method on top of PrintStream's printf(String, Object[]).
     * </p>
     *
     * @param format is the format specifier.
     * @param args are the arguments to substitute into the format string.
     */
    public static void printf(final String format,
                              final Iterable<Object> args)
    {
        Preconditions.checkNotNull(format);
        Preconditions.checkNotNull(args);

        System.out.printf(format, Lists.newLinkedList(args).toArray());
    }

    /**
     * This method prints a formated string to standard-error.
     *
     * <p>
     * This is simply a convenience method on top of PrintStream's printf(String, Object[]).
     * </p>
     *
     * @param format is the format specifier.
     * @param args are the arguments to substitute into the format string.
     */
    public static void printerrf(final String format,
                                 final Iterable<Object> args)
    {
        Preconditions.checkNotNull(format);
        Preconditions.checkNotNull(args);

        System.out.printf(format, Lists.newLinkedList(args).toArray());
    }

    /**
     * This method reads a line of text from standard-input.
     *
     * @return the line of text.
     */
    public static String readln()
    {
        final Scanner scanner = new Scanner(System.in);

        final String input = scanner.nextLine();

        return input;
    }

    /**
     * This method creates an iterable that iterates over the integers in a discrete range.
     *
     * @param start is the inclusive lowest value in the range.
     * @param end is the inclusive highest value in the range.
     * @param step is the distance that elements in the range are separated for one another.
     * @return a lazy list of integers.
     * @throws IllegalArgumentException if end is less-than start.
     */
    public static Iterable<Integer> range(final int start,
                                          final int end,
                                          final int step)
    {
        // TODO: test

        Preconditions.checkArgument(start <= end);

        return new Iterable<Integer>()
        {
            @Override
            public Iterator<Integer> iterator()
            {
                return new Iterator<Integer>()
                {
                    private int index = start;

                    @Override
                    public boolean hasNext()
                    {
                        return index <= end;
                    }

                    @Override
                    public Integer next()
                    {
                        final int result = index;

                        index = index + step;

                        return result;
                    }

                    @Override
                    public void remove()
                    {
                        throw new UnsupportedOperationException("Sorry, you cannot remove a number from the number line.");
                    }
                };
            }
        };
    }

    /**
     * This method creates an iterator over a given array of booleans.
     *
     * <p>
     * The returned iterator will not support the remove() method.
     * </p>
     *
     * @param array is the array itself.
     * @return an iterable whose iterator can iterate over the given array.
     */
    public static List<Boolean> iter(final boolean[] array)
    {
        /**
         * Create a list that wraps the array.
         */
        final ArrayBackedList<Boolean> list = new ArrayBackedList<Boolean>()
        {
            @Override
            public Boolean get(int index)
            {
                return array[index];
            }

            @Override
            public int size()
            {
                return array.length;
            }
        };

        /**
         * Make sure the list is unmodifiable.
         */
        return Collections.unmodifiableList(list);
    }

    /**
     * This method creates an iterator over a given array of chars.
     *
     * <p>
     * The returned iterator will not support the remove() method.
     * </p>
     *
     * @param array is the array itself.
     * @return an iterable whose iterator can iterate over the given array.
     */
    public static List<Character> iter(final char[] array)
    {
        /**
         * Create a list that wraps the array.
         */
        final ArrayBackedList<Character> list = new ArrayBackedList<Character>()
        {
            @Override
            public Character get(int index)
            {
                return array[index];
            }

            @Override
            public int size()
            {
                return array.length;
            }
        };

        /**
         * Make sure the list is unmodifiable.
         */
        return Collections.unmodifiableList(list);
    }

    /**
     * This method creates an iterator over a given array of bytes.
     *
     * <p>
     * The returned iterator will not support the remove() method.
     * </p>
     *
     * @param array is the array itself.
     * @return an iterable whose iterator can iterate over the given array.
     */
    public static List<Byte> iter(final byte[] array)
    {
        /**
         * Create a list that wraps the array.
         */
        final ArrayBackedList<Byte> list = new ArrayBackedList<Byte>()
        {
            @Override
            public Byte get(int index)
            {
                return array[index];
            }

            @Override
            public int size()
            {
                return array.length;
            }
        };

        /**
         * Make sure the list is unmodifiable.
         */
        return Collections.unmodifiableList(list);
    }

    /**
     * This method creates an iterator over a given array of shorts.
     *
     * <p>
     * The returned iterator will not support the remove() method.
     * </p>
     *
     * @param array is the array itself.
     * @return an iterable whose iterator can iterate over the given array.
     */
    public static List<Short> iter(final short[] array)
    {
        /**
         * Create a list that wraps the array.
         */
        final ArrayBackedList<Short> list = new ArrayBackedList<Short>()
        {
            @Override
            public Short get(int index)
            {
                return array[index];
            }

            @Override
            public int size()
            {
                return array.length;
            }
        };

        /**
         * Make sure the list is unmodifiable.
         */
        return Collections.unmodifiableList(list);
    }

    /**
     * This method creates an iterator over a given array of ints.
     *
     * <p>
     * The returned iterator will not support the remove() method.
     * </p>
     *
     * @param array is the array itself.
     * @return an iterable whose iterator can iterate over the given array.
     */
    public static List<Integer> iter(final int[] array)
    {
        /**
         * Create a list that wraps the array.
         */
        final ArrayBackedList<Integer> list = new ArrayBackedList<Integer>()
        {
            @Override
            public Integer get(int index)
            {
                return array[index];
            }

            @Override
            public int size()
            {
                return array.length;
            }
        };

        /**
         * Make sure the list is unmodifiable.
         */
        return Collections.unmodifiableList(list);
    }

    /**
     * This method creates an iterator over a given array of longs.
     *
     * <p>
     * The returned iterator will not support the remove() method.
     * </p>
     *
     * @param array is the array itself.
     * @return an iterable whose iterator can iterate over the given array.
     */
    public static List<Long> iter(final long[] array)
    {
        /**
         * Create a list that wraps the array.
         */
        final ArrayBackedList<Long> list = new ArrayBackedList<Long>()
        {
            @Override
            public Long get(int index)
            {
                return array[index];
            }

            @Override
            public int size()
            {
                return array.length;
            }
        };

        /**
         * Make sure the list is unmodifiable.
         */
        return Collections.unmodifiableList(list);
    }

    /**
     * This method creates an iterator over a given array of floats.
     *
     * <p>
     * The returned iterator will not support the remove() method.
     * </p>
     *
     * @param array is the array itself.
     * @return an iterable whose iterator can iterate over the given array.
     */
    public static List<Float> iter(final float[] array)
    {
        /**
         * Create a list that wraps the array.
         */
        final ArrayBackedList<Float> list = new ArrayBackedList<Float>()
        {
            @Override
            public Float get(int index)
            {
                return array[index];
            }

            @Override
            public int size()
            {
                return array.length;
            }
        };

        /**
         * Make sure the list is unmodifiable.
         */
        return Collections.unmodifiableList(list);
    }

    /**
     * This method creates an iterator over a given array of doubles.
     *
     * <p>
     * The returned iterator will not support the remove() method.
     * </p>
     *
     * @param array is the array itself.
     * @return an iterable whose iterator can iterate over the given array.
     */
    public static List<Double> iter(final double[] array)
    {
        /**
         * Create a list that wraps the array.
         */
        final ArrayBackedList<Double> list = new ArrayBackedList<Double>()
        {
            @Override
            public Double get(int index)
            {
                return array[index];
            }

            @Override
            public int size()
            {
                return array.length;
            }
        };

        /**
         * Make sure the list is unmodifiable.
         */
        return Collections.unmodifiableList(list);
    }

    /**
     * This method creates an iterator over a given array of objects.
     *
     * <p>
     * The returned iterator will not support the remove() method.
     * </p>
     *
     * @param array is the array itself.
     * @return an iterable whose iterator can iterate over the given array.
     */
    public static <T> List<T> iter(final T[] array)
    {
        /**
         * Create a list that wraps the array.
         */
        final ArrayBackedList<T> list = new ArrayBackedList<T>()
        {
            @Override
            public T get(int index)
            {
                return array[index];
            }

            @Override
            public int size()
            {
                return array.length;
            }
        };

        /**
         * Make sure the list is unmodifiable.
         */
        return Collections.unmodifiableList(list);
    }

    /**
     * This method creates an iterator over an iterable.
     *
     * @param iterable is the iterable itself.
     * @return iterable.
     */
    public static <T> List<T> iter(final Iterable<T> iterable)
    {
        /**
         * Special Case: The iterable is a list.
         */
        if (iterable instanceof List)
        {
            return Collections.unmodifiableList((List) iterable);
        }

        /**
         * Create a list that wraps the array.
         */
        final ArrayBackedList<T> list = new ArrayBackedList<T>()
        {
            @Override
            public T get(int index)
            {
                return Iterables.get(iterable, index);
            }

            @Override
            public int size()
            {
                return Iterables.size(iterable);
            }

            @Override
            public Iterator<T> iterator()
            {
                return Iterators.unmodifiableIterator(iterable.iterator());
            }
        };

        /**
         * Make sure the list is unmodifiable.
         */
        return Collections.unmodifiableList(list);
    }

    /**
     * This method creates an iterator over an iterator.
     *
     * @param iterable is the iterator itself.
     * @return an iterable that wraps the iterator.
     */
    public static <T> Iterable<T> iter(final Iterator<T> iterable)
    {
        return new Iterable<T>()
        {
            @Override
            public Iterator<T> iterator()
            {
                return iterable;
            }
        };
    }

    public static List<String> iter(final Annotation anno)
    {
        try
        {
            Preconditions.checkNotNull(anno);

            /**
             * The values stored in the annotation will be added to this list.
             */
            final List<String> result = new LinkedList();

            /**
             * If the annotation is an Autumn-style annotation,
             * then it must have a value() : String[] method.
             */
            final Method method = F.findMethod(anno.annotationType(), "value", Collections.EMPTY_LIST);

            /**
             * If the annotation is not an Autumn-style annotation,
             * then we cannot extract any values from it.
             */
            if (method == null || method.getReturnType() != String[].class)
            {
                return Collections.EMPTY_LIST;
            }

            /**
             * Extract the values from the annotation.
             */
            final String[] values = (String[]) method.invoke(anno);
            result.addAll(Arrays.asList(values));

            /**
             * Return the result as an immutable list.
             */
            return Collections.unmodifiableList(result);
        }
        catch (IllegalAccessException ex)
        {
            return Collections.EMPTY_LIST;
        }
        catch (InvocationTargetException ex)
        {
            return Collections.EMPTY_LIST;
        }
    }

    /**
     * This method returns the default value of a given type.
     *
     * <p>
     * The boolean type returns false. <br>
     * Numeric types return zero. <br>
     * Reference types return null. <br>
     * </p>
     *
     * @param type is the type that indicates the value to return.
     * @return
     * @throws NullPointerException if the type is null.
     * @throws IllegalArgumentException if the type is the void-type.
     */
    public static Object defaultValue(final Class type)
    {
        Preconditions.checkNotNull(type);
        Preconditions.checkArgument(type != void.class,
                                    "The void-type does not have a default value.");

        if (type == boolean.class)
        {
            return false;
        }
        else if (type == char.class)
        {
            return (char) 0;
        }
        else if (type == byte.class)
        {
            return (byte) 0;
        }
        else if (type == short.class)
        {
            return (short) 0;
        }
        else if (type == int.class)
        {
            return (int) 0;
        }
        else if (type == long.class)
        {
            return (long) 0;
        }
        else if (type == float.class)
        {
            return (float) 0;
        }
        else if (type == double.class)
        {
            return (double) 0;
        }
        else // reference types
        {
            return null;
        }
    }

    /**
     * This method never returns the same BigInteger twice.
     *
     * @return a new unique positive integer.
     */
    public static synchronized BigInteger unique()
    {
        final BigInteger result = unique;

        unique = unique.add(BigInteger.ONE);

        return result;
    }

    /**
     * This method compares to comparable values.
     *
     * <p>
     * A result of negative one indicates that the left operand is less than the right operand.
     * A result of positive one indicates that the left operand is greater than the right operand.
     * A zero result indicates that the operands are equal.
     * </p>
     *
     * </p>
     * This method considers null to be less than an object.
     * </p>
     *
     * @param left is the left operand.
     * @param right is the right operand.
     * @return an integer that represents the relationship between the operands.
     */
    public static int compare(final Comparable left,
                              final Comparable right)
    {
        if (left == null && right == null)
        {
            return 0;
        }
        else if (left == null && right != null)
        {
            return -1;
        }
        else if (left != null && right == null)
        {
            return 1;
        }
        else
        {
            final int relationship = ((Comparable) left).compareTo(right);

            if (relationship == 0)
            {
                return 0;
            }
            else if (relationship > 0)
            {
                return 1;
            }
            else // relationship < 0
            {
                return -1;
            }
        }
    }

    /**
     * This method calculates the sum of a set of values.
     *
     * @param values are the values to sum.
     * @return the sum of the values.
     */
    public static BigDecimal sum(final Iterable<?> values)
    {
        BigDecimal total = F.big(BigDecimal.ZERO);

        for (Object value : values)
        {
            Preconditions.checkNotNull(value);

            total = total.add(F.big(value));
        }

        return total;
    }

    /**
     * This method calculates the average of a set of values.
     *
     * @param values are the values to average.
     * @return the average of the values.
     */
    public static BigDecimal average(final Iterable<?> values)
    {
        assert values != null;

        BigDecimal total = BigDecimal.ZERO;

        int count = 0;

        for (Object value : values)
        {
            final BigDecimal big = F.big(value);

            total = total.add(big);
            ++count;
        }

        final BigDecimal result = total.divide(F.big(count));

        return F.big(result);
    }

    /**
     * This method searches for the minimum value in a set of values.
     *
     * @param values are the values to search through.
     * @return the minimum value in the group.
     * @throws NullPointerException if values is null.
     */
    public static Comparable minimum(final Iterable<Comparable> values)
    {
        Preconditions.checkNotNull(values);

        final Iterator<Comparable> iter = values.iterator();

        /**
         * If there are no values, then return null;
         * otherwise, return the minimum value.
         */
        if (iter.hasNext() == false)
        {
            return null;
        }
        else
        {
            Comparable least = iter.next();

            while (iter.hasNext())
            {
                final Comparable value = iter.next();

                least = compare(value, least) < 0 ? value : least;
            }

            return least;
        }
    }

    /**
     * This method searches for the maximum value in a set of values.
     *
     * @param values are the values to search through.
     * @return the maximum value in the group.
     * @throws NullPointerException if values is null.
     */
    public static Comparable maximum(final Iterable<Comparable> values)
    {
        Preconditions.checkNotNull(values);

        final Iterator<Comparable> iter = values.iterator();

        /**
         * If there are no values, then return null;
         * otherwise, return the maximum value.
         */
        if (iter.hasNext() == false)
        {
            return null;
        }
        else
        {
            Comparable greatest = iter.next();

            while (iter.hasNext())
            {
                final Comparable value = iter.next();

                greatest = compare(value, greatest) > 0 ? value : greatest;
            }

            return greatest;
        }
    }

    /**
     * This method determines whether any of the values in an iterable match a predicate.
     *
     * @param values are the values to check.
     * @param condition is the condition that may match one of the values.
     * @return true, iff at least one of the values matches the predicate.
     */
    public static boolean any(final Iterable<?> values,
                              final Predicate condition)
            throws Throwable
    {
        for (Object value : values)
        {
            if (condition.invoke(value))
            {
                return true;
            }
        }

        return false;
    }

    /**
     * This method determines whether all of the values in an iterable match a predicate.
     *
     * @param values are the values to check.
     * @param condition is the condition that may match the values.
     * @return true, iff all of the values match the predicate.
     */
    public static boolean all(final Iterable<?> values,
                              final Predicate condition)
            throws Throwable
    {
        for (Object value : values)
        {
            if (condition.invoke(value) == false)
            {
                return false;
            }
        }

        return true;
    }

    /**
     * This method counts the the values that match a predicate
     *
     * @param values are the values to check.
     * @param condition is the condition that may match some of the values.
     * @return the number of values (may be zero) that match the predicate.
     */
    public static int count(final Iterable<?> values,
                            final Predicate condition)
            throws Throwable
    {
        int count = 0;

        for (Object value : values)
        {
            if (condition.invoke(value))
            {
                ++count;
            }
        }

        return count;
    }

    /**
     * This method applies a functor to each element of an iterable and collects the results.
     *
     * @param iterable is the iterable that provides input for the functor.
     * @param function is the functor(Object) : Object that processes the input.
     * @return an immutable list containing the output of the functor.
     * @throws Throwable in order to allow exceptions to propagate from the functor.
     */
    public static List<Object> map(final Iterable iterable,
                                   final Function1 function)
            throws Throwable
    {
        Preconditions.checkNotNull(iterable);
        Preconditions.checkNotNull(function);

        final List<Object> output = Lists.newLinkedList();

        for (Object element : iterable)
        {
            output.add(T.apply(function, Collections.singletonList(element)));
        }

        return Collections.unmodifiableList(output);
    }

    public static List<Object> filter(final Iterable<?> iterable,
                                      final Predicate function)
            throws Throwable
    {
        Preconditions.checkNotNull(iterable);
        Preconditions.checkNotNull(function);

        final List<Object> result = new LinkedList();

        for (Object value : iterable)
        {
            if (function.invoke(value))
            {
                result.add(value);
            }
        }

        return Collections.unmodifiableList(result);
    }

    public static Object reduce(final Iterable iterable,
                                final Function2 functor)
            throws Throwable
    {
        return null;
    }

    public static Object find(final Iterable<?> iterable,
                              final Predicate function,
                              final int skip)
            throws Throwable
    {
        Preconditions.checkNotNull(iterable);
        Preconditions.checkNotNull(function);
        Preconditions.checkArgument(skip >= 0);

        int count = 0;

        for (Object value : iterable)
        {
            final boolean match = function.invoke(value);

            if (match && count == skip)
            {
                return value;
            }
            else if (match && count != skip)
            {
                ++count;
            }
        }

        return null;
    }

    /**
     * This method creates a sorted list from an unsorted iterable.
     *
     * <p>
     * If an in-place sort is preferred, consider using method
     * <code>sort(List)</code> in class
     * <code>java.util.Collections</code>.
     * </p>
     *
     * @param iterable provides the elements for the new sorted list.
     * @return a sorted list that contains the elements of the given input iterable.
     */
    public static <T extends Comparable> List<T> sorted(final Iterable<T> iterable)
    {
        Preconditions.checkNotNull(iterable);

        final ArrayList<T> result = Lists.newArrayList(iterable);

        Collections.sort(result);

        return result;
    }

    /**
     * This method creates a sorted list from an unsorted iterable.
     *
     * <p>
     * If an in-place sort is preferred, consider using method
     * <code>sort(List)</code> in class
     * <code>java.util.Collections</code>.
     * </p>
     *
     * @param iterable provides the elements for the new sorted list.
     * @param comparator is a function object that can compare elements of the iterable.
     * @return a sorted list that contains the elements of the given input iterable.
     */
    public static <T> List<T> sorted(final Iterable<T> iterable,
                                     final Comparator<T> comparator)
    {
        Preconditions.checkNotNull(iterable);

        final ArrayList<T> result = Lists.newArrayList(iterable);

        Collections.sort(result, comparator);

        return result;
    }

    /**
     * This method replaces escape-sequences in a string with the characters they represent.
     *
     * @param string the string that may contain escape-sequences.
     * @return the modified string.
     * @throws NullPointerException if string is null.
     * @throws IllegalArgumentException if an escape-sequence is malformed.
     */
    public static String escape(final CharSequence string)
    {
        Preconditions.checkNotNull(string);

        // This is the string being built.
        final StringBuilder result = new StringBuilder();

        // This is true, when an escape-sequence is being processed.
        boolean in_escape = false;

        // This is the number of characters to advance forward.
        int increment;

        for (int i = 0; i < string.length(); i = i + increment)
        {
            increment = 1;

            final char chr = string.charAt(i);

            if (in_escape == false && chr == '\\')
            {
                in_escape = true;
            }
            else if (in_escape && chr == 't')
            {
                result.append('\t');
                in_escape = false;
            }
            else if (in_escape && chr == 'b')
            {
                result.append('\b');
                in_escape = false;
            }
            else if (in_escape && chr == 'n')
            {
                result.append('\n');
                in_escape = false;
            }
            else if (in_escape && chr == 'r')
            {
                result.append('\r');
                in_escape = false;
            }
            else if (in_escape && chr == 'f')
            {
                result.append('\f');
                in_escape = false;
            }
            else if (in_escape && chr == '\'')
            {
                result.append('\'');
                in_escape = false;
            }
            else if (in_escape && chr == '"')
            {
                result.append('\"');
                in_escape = false;
            }
            else if (in_escape && chr == '\\')
            {
                result.append('\\');
                in_escape = false;
            }
            else if (in_escape && string.length() >= i + 5)
            {
                /**
                 * Get the character-code, which consist of five digits.
                 */
                final String escape_string = string.subSequence(i, i + 5).toString();

                /**
                 * The string must contain exactly five digits.
                 */
                if (!escape_string.matches("[0-9][0-9][0-9][0-9][0-9]"))
                {
                    Preconditions.checkArgument(false, "Invalid Escape Sequence: \\" + escape_string);
                }

                /**
                 * The character-code must be an integer in range [0, Character.MAX_VALUE].
                 */
                final int value = Integer.parseInt(escape_string);

                if (value < 0 || value > Character.MAX_VALUE)
                {
                    Preconditions.checkArgument(false, "Invalid Escape Sequence: \\" + escape_string);
                }

                /**
                 * Add the specified character to the string.
                 */
                final char character = (char) value;
                result.append(character);

                /**
                 * Skip over the escape-string and exit the escape-sequence.
                 */
                increment = increment + 4;
                in_escape = false;
            }
            else if (in_escape)
            {
                Preconditions.checkArgument(false, "Invalid Escape Sequence: \\" + chr);
            }
            else
            {
                result.append(chr);
            }
        }

        return result.toString();
    }

    /**
     * This method replaces escapable characters with escape-sequences.
     *
     * @param string is the string to un-escape.
     * @return the string with escapable characters replaced with escape-sequences.
     */
    public static String unescape(final CharSequence string)
    {
        Preconditions.checkNotNull(string);

        // These are the characters that can be replaced with an escape-sequence.
        final String escapable = "\t\b\n\r\f\'\"\\";

        final String escaped = "tbnrf'\"\\";

        final StringBuilder result = new StringBuilder();

        for (int i = 0; i < string.length(); i++)
        {
            final char chr = string.charAt(i);

            if (escapable.indexOf(chr) >= 0)
            {
                result.append('\\');
                result.append(escaped.charAt(escapable.indexOf(chr)));
            }
            else if (chr == 0)
            {
                result.append("\\00000");
            }
            else
            {
                result.append(chr);
            }
        }

        return result.toString();
    }

    /**
     * This method pads the start of a string.
     *
     * @param string is the string to pad.
     * @param character is the padding character.
     * @param length is the required minimum length of the string.
     * @return the padded string.
     */
    public static String padStart(final CharSequence string,
                                  final char character,
                                  final int length)
    {
        Preconditions.checkNotNull(string);
        Preconditions.checkArgument(length >= 0);

        final int size = string.length();

        final StringBuilder result = new StringBuilder();

        // Add the padding characters.
        while ((result.length() + size) < length)
        {
            result.append(character);
        }

        result.append(string);

        return result.toString();
    }

    /**
     * This method pads the end of a string.
     *
     * @param string is the string to pad.
     * @param character is the padding character.
     * @param length is the required minimum length of the string.
     * @return the padded string.
     */
    public static String padEnd(final CharSequence string,
                                final char character,
                                final int length)
    {
        Preconditions.checkNotNull(string);
        Preconditions.checkArgument(length >= 0);

        final int size = string.length();

        final StringBuilder result = new StringBuilder(string);

        // Add the padding characters.
        while ((result.length() + size) < length)
        {
            result.append(character);
        }

        return result.toString();
    }

    /**
     * This method pads the beginning of a string with zeros, if needed.
     *
     * @param string is the string to possibly pad.
     * @param length is the desired minimum length of the result.
     * @return the padded string.
     */
    public static String zfill(final CharSequence string,
                               final int length)
    {
        return padStart(string, '0', length);
    }

    /**
     * This method creates a string representation for an iterable.
     *
     * @param iterable is the iterable itself.
     * @param prefix is a string to prepend onto the result.
     * @param separator is the substring used to separate elements in the result.
     * @param suffix is a string to append onto the result.
     * @return the aforedescribed result.
     */
    public static String str(final Iterable<?> iterable,
                             final String prefix,
                             final String separator,
                             final String suffix)
    {
        Preconditions.checkNotNull(iterable);
        Preconditions.checkNotNull(prefix);
        Preconditions.checkNotNull(separator);
        Preconditions.checkNotNull(suffix);

        final List<?> elements = Lists.newArrayList(iterable);

        final StringBuilder result = new StringBuilder();

        result.append(prefix);
        {
            int count = 0;

            for (Object arg : elements)
            {
                ++count;

                result.append(arg);

                if (count < elements.size())
                {
                    result.append(separator);
                }
            }
        }
        result.append(suffix);

        return result.toString();
    }

    /**
     * This method creates a string representation for a map.
     *
     * @param map is the map itself.
     * @param prefix is a string to prepend onto the result.
     * @param assign is the substring to place between keys and values in the result.
     * @param seperator is the substring used to separate key-value-pairs from each other in the result.
     * @param suffix is a string to append onto the result.
     * @return the aforedescribed result.
     */
    public static String str(final Map<?, ?> map,
                             final String prefix,
                             final String assign,
                             final String separator,
                             final String suffix)
    {
        Preconditions.checkNotNull(map);
        Preconditions.checkNotNull(prefix);
        Preconditions.checkNotNull(assign);
        Preconditions.checkNotNull(separator);
        Preconditions.checkNotNull(suffix);

        final StringBuilder result = new StringBuilder();

        result.append(prefix);
        {
            int count = 0;

            for (Entry<?, ?> entry : map.entrySet())
            {
                ++count;

                result.append(entry.getKey());
                result.append(assign);
                result.append(entry.getValue());

                if (count < map.size())
                {
                    result.append(separator);
                }
            }
        }
        result.append(suffix);

        return result.toString();
    }

    /**
     * This method synchronizes the invocation of a functor.
     *
     * @param locked is the object to obtain a lock on.
     * @param body is the functor to invoke.
     * @throws Throwable if an exception is thrown by the functor.
     * @throws IllegalArgumentException if the functor is non-nullary.
     */
    public static void sync(final Object locked,
                            final Action body)
            throws Throwable
    {
        Preconditions.checkNotNull(body);
        Preconditions.checkArgument(body.parameterTypes().isEmpty(),
                                    "The functor cannot take any parameters.");

        synchronized (locked)
        {
            T.apply(body, Collections.EMPTY_LIST);
        }
    }

    /**
     * This method sets the value of each element to its default value.
     *
     * <p>
     * This method does not affect the special-method bindings.
     * </p>
     *
     * @return this object, if this record is mutable; otherwise, return a modified copy thereof.
     */
    @Infer
    public static <T extends Record> T clear(final T self)
    {
        T p = self;

        final List<Class> types = self.types();

        for (int i = 0; i < self.size(); i++)
        {
            p = (T) p.set(i, F.defaultValueOf(types.get(i)));
        }

        return p;
    }

    /**
     * This method assigns a new value to a specific element.
     *
     * <p>
     * Auto-unboxing will be performed, if necessary.
     * However, the unboxed value will not be auto-widened.
     * </p>
     *
     * <p>
     * This is a linear-time operation in the worst case.
     * </p>
     *
     * @param key is the name of the element to assign the value to.
     * @param value is the new value.
     * @return this object, if this record is mutable; otherwise, return a modified copy thereof.
     * @throws NullPointerException if the key is null.
     * @throws NoSuchElementException if the key does not refer to an actual element.
     * @throws ClassCastException if the object is not of an acceptable type.
     */
    @Infer
    public static <T extends Record> T set(final T self,
                                           final String key,
                                           final Object value)
    {
        Preconditions.checkNotNull(key);

        final int index = self.keys().indexOf(key);

        if (index < 0)
        {
            throw new NoSuchElementException("Key: " + key);
        }

        final Record result = self.set(index, value);

        return (T) result;
    }

    /**
     * This method retrieves the value of a specific element.
     *
     * <p>
     * This is a linear-time operation in the worst case.
     * </p>
     *
     * @param key is the name of the element to retrieve.
     * @return the value of the element.
     * @throws NullPointerException if the key is null.
     * @throws NoSuchElementException if no element is identified by the given key.
     */
    public static Object get(final Record self,
                             final String key)
    {
        Preconditions.checkNotNull(key);

        final int index = self.keys().indexOf(key);

        if (index < 0)
        {
            throw new NoSuchElementException("Key: " + key);
        }

        final Object result = self.get(index);

        return result;
    }

    /**
     * This method determines whether the set of entries in one record
     * is a subset of the records in another record.
     *
     * @param subtype is the possible subset record.
     * @param supertype is the possible superset record.
     * @return true, if subtype.keys() is a subset of supertype.keys().
     */
    public static boolean isSubset(final Record subtype,
                                   final Record supertype)
    {
        Preconditions.checkNotNull(subtype);
        Preconditions.checkNotNull(supertype);

        final boolean answer = subtype.keys().containsAll(supertype.keys());

        return answer;
    }

    @Infer
    public static <T extends Record> T set(final T assignee,
                                           final Map<Object, Object> value)
    {
        Preconditions.checkNotNull(assignee, "The assignee cannot be null.");
        Preconditions.checkNotNull(value, "The value cannot be null.");

        T p = assignee;

        for (Entry<Object, Object> entry : value.entrySet())
        {
            if (entry.getKey() instanceof Integer)
            {
                p = (T) F.set(p, (Integer) entry.getKey(), entry.getValue());
            }
            else
            {
                p = (T) F.set(p, (String) entry.getKey(), entry.getValue());
            }
        }

        return p;
    }

    @Infer
    public static <T extends Record> T set(final T assignee,
                                           final Record value)
    {
        Preconditions.checkNotNull(assignee, "The assignee cannot be null.");
        Preconditions.checkNotNull(value, "The value cannot be null.");

        T p = assignee;

        for (String key : value.keys())
        {
            p = (T) F.set(p, key, value);
        }

        return p;
    }

    public static String json(final Object input)
    {
        final JsonEncoder encoder = new JsonEncoder();

        final String output = encoder.encode(input);

        return output;
    }

    public static Object json(final String input,
                              final Module module)
    {
        final JsonDecoder decoder = new JsonDecoder();

        final Object result = decoder.decode(module, input);

        return result;
    }

    public static void remember(final Object target,
                                final Object key,
                                final Object value)
    {
    }

    public static Object recall(final Object target,
                                final Object key)
    {
        return null;
    }

    public static Field findField(final Class owner,
                                  final String name)
    {
        Preconditions.checkNotNull(owner);
        Preconditions.checkNotNull(name);

        try
        {
            return owner.getField(name);
        }
        catch (Exception ex)
        {
            return null;
        }
    }

    public static Constructor findConstructor(final Class owner,
                                              final Iterable<Class> formals)
    {
        Preconditions.checkNotNull(owner);
        Preconditions.checkNotNull(formals);

        final Class[] params = (Class[]) F.immutable(formals).toArray(new Class[0]);

        try
        {
            return owner.getConstructor(params);
        }
        catch (Exception ex)
        {
            return null;
        }
    }

    public static Method findMethod(final Class owner,
                                    final String name,
                                    final Iterable<Class> formals)
    {
        Preconditions.checkNotNull(owner);
        Preconditions.checkNotNull(name);
        Preconditions.checkNotNull(formals);

        final Class[] params = (Class[]) F.immutable(formals).toArray(new Class[0]);

        try
        {
            return owner.getMethod(name, params);
        }
        catch (Exception ex)
        {
            return null;
        }
    }

    /**
     * TODO: This must be a weak map.
     */
    private static final Map<Object, Map<Object, Object>> details = Maps.newIdentityHashMap();

    /**
     * This method associates a detail with a given exception.
     *
     * @param problem is the exception that the detail is in reference to.
     * @param key identifies the detail.
     * @param value is the content of the detail.
     * @throws NullPointerException if problem is null.
     * @throws NullPointerException if key is null.
     */
    public static void set(final Throwable problem,
                           final String key,
                           final Object value)
    {
        Preconditions.checkNotNull(problem);
        Preconditions.checkNotNull(key);

        if (details.containsKey(problem) == false)
        {
            details.put(problem, Maps.newHashMap());
        }

        final Map<Object, Object> map = details.get(problem);

        map.put(key, value);
    }

    /**
     * This method retrieves a detail that is associated with a given exception.
     *
     * @param problem is the exception that the detail is in reference to.
     * @param key identifies the detail.
     * @return the value of the detail, or null, if the detail does not exist.
     * @throws NullPointerException if problem is null.
     * @throws NullPointerException if key is null.
     */
    public static Object get(final Throwable problem,
                             final String key)
    {
        Preconditions.checkNotNull(problem);
        Preconditions.checkNotNull(key);

        if (details.containsKey(problem) == false)
        {
            return null;
        }

        final Map<Object, Object> map = details.get(problem);

        final Object result = map.get(key);

        return result;
    }

    /**
     * This method converts a value to a BigDecimal.
     *
     * @param value is the value to convert.
     * @return the value as a BigDecimal.
     * @throws NullPointerException if value is null.
     */
    public static BigDecimal big(final Object value)
    {
        Preconditions.checkNotNull(value);

        /**
         * Convert the value to a BigDecimal.
         */
        final BigDecimal result;

        if (value instanceof Character)
        {
            result = BigDecimal.valueOf((long) (Character) value);
        }
        else if (value instanceof Byte)
        {
            result = BigDecimal.valueOf((long) (Byte) value);
        }
        else if (value instanceof Short)
        {
            result = BigDecimal.valueOf((long) (Short) value);
        }
        else if (value instanceof Integer)
        {
            result = BigDecimal.valueOf((long) (Integer) value);
        }
        else if (value instanceof Long)
        {
            result = BigDecimal.valueOf((long) (Long) value);
        }
        else if (value instanceof Float)
        {
            result = BigDecimal.valueOf((double) (Float) value);
        }
        else if (value instanceof Double)
        {
            result = BigDecimal.valueOf((double) (Double) value);
        }
        else if (value instanceof BigInteger)
        {
            result = new BigDecimal((BigInteger) value);
        }
        else if (value instanceof BigDecimal)
        {
            result = (BigDecimal) value;
        }
        else
        {
            throw new IllegalArgumentException("The value cannot be converted to a BigDecimal.");
        }

        /**
         * Ensure that the scale of the result is correct.
         */
        final BigDecimal datum = Helpers.createBigDecimal(result);

        /**
         * Return the value as a BigDecimal.
         */
        return datum;
    }

    /**
     * This method creates a list of [index, element] pairs.
     *
     * @param iterable provides the elements.
     * @return the list of [index, element] pairs.
     * @throws NullPointerException if iterable is null.
     */
    public static List<List<Object>> enumerate(final Iterable<?> iterable)
    {
        Preconditions.checkNotNull(iterable);

        final List<List<Object>> pairs = Lists.newLinkedList();

        int index = 0;

        for (Object value : iterable)
        {
            final List<Object> pair = Lists.newArrayList(index++, value);

            pairs.add(Collections.unmodifiableList(pair));
        }

        return Collections.unmodifiableList(pairs);
    }

    public static AsyncTask async(final Action action)
    {
        return null;
    }

    public static boolean isAssignableTo(final Class T,
                                         final Class X)
    {
        /**
         * Case: X is T
         */
        if (Operators.equals(T, X))
        {
            return true;
        }

        /**
         * Case: X is null
         */
        if (X == null && Object.class.isAssignableFrom(T))
        {
            return true;
        }

        /**
         * From now on, T cannot be the null-type or the void-type.
         * Likewise, X cannot be the void-type.
         */
        if (T == null || T == void.class || X == void.class)
        {
            return false;
        }

        /**
         * Case: Boxing
         */
        final boolean boxing = (X == boolean.class && T == Boolean.class)
                               || (X == char.class && T == Character.class)
                               || (X == byte.class && T == Byte.class)
                               || (X == short.class && T == Short.class)
                               || (X == int.class && T == Integer.class)
                               || (X == long.class && T == Long.class)
                               || (X == float.class && T == Float.class)
                               || (X == double.class && T == Double.class)
                               || (X != null && X.isPrimitive() && T == Object.class)
                               || (X != null && X.isPrimitive() && T == Comparable.class)
                               || (X != null && X.isPrimitive() && X != boolean.class && X != char.class && T == Number.class);

        if (boxing)
        {
            return true;
        }


        /**
         * Case: Unboxing
         */
        final boolean unboxing = (X == Boolean.class && T == boolean.class)
                                 || (X == Character.class && T == char.class)
                                 || (X == Byte.class && T == byte.class)
                                 || (X == Short.class && T == short.class)
                                 || (X == Integer.class && T == int.class)
                                 || (X == Long.class && T == long.class)
                                 || (X == Float.class && T == float.class)
                                 || (X == Double.class && T == double.class);

        if (unboxing)
        {
            return true;
        }

        /**
         * Case: Coercion
         */
        final boolean coercion = (X == char.class && T == int.class)
                                 || (X == char.class && T == long.class)
                                 || (X == byte.class && T == short.class)
                                 || (X == byte.class && T == int.class)
                                 || (X == byte.class && T == long.class)
                                 || (X == short.class && T == int.class)
                                 || (X == short.class && T == long.class)
                                 || (X == int.class && T == long.class)
                                 || (X == float.class && T == double.class);

        if (coercion)
        {
            return true;
        }

        /**
         * Case: Subtyping
         */
        if (isSubtypeOf(X, T))
        {
            return true;
        }

        /**
         * Case: Not Assignable
         */
        return false;
    }

    public static boolean isSubtypeOf(final Class subtype,
                                      final Class supertype)
    {
        if (Operators.equals(subtype, supertype))
        {
            return true;
        }
        else if (subtype == null && Object.class.isAssignableFrom(supertype))
        {
            return true;
        }
        else if (subtype != null && supertype != null && supertype.isAssignableFrom(subtype))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}