1400555
import java.util.*;
import java.util.zip.*;
import java.util.List;
import java.util.regex.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
import java.util.function.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.table.*;
import java.io.*;
import java.net.*;
import java.lang.reflect.*;
import java.lang.ref.*;
import java.lang.management.*;
import java.security.*;
import java.security.spec.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.imageio.*;
import java.math.*;
import java.time.Duration;
import java.lang.invoke.VarHandle;
import java.lang.invoke.MethodHandles;
import java.awt.geom.*;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import static x30_pkg.x30_util.DynamicObject;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.Const;
import org.apache.bcel.generic.*;
import java.lang.reflect.Type;
import java.util.jar.*;
import java.text.*;
import java.text.NumberFormat;
import java.nio.charset.Charset;
import java.util.TimeZone;
import java.nio.file.Path;
import java.text.SimpleDateFormat;

public class main {

  static public void test_leftArrowScript_scriptError() {
    Throwable error = (Throwable) (leftArrow("getException < Runnable fail"));
    var scriptError = innerExceptionOfType(GazelleV_LeftArrowScript.ScriptError.class, error);
    assertNotNullVerbose(scriptError);
  }

  static public Method findMethod(Object o, String method, Object... args) {
    return findMethod_cached(o, method, args);
  }

  static public boolean findMethod_checkArgs(Method m, Object[] args, boolean debug) {
    Class<?>[] types = m.getParameterTypes();
    if (types.length != args.length) {
      if (debug)
        System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
      return false;
    }
    for (int i = 0; i < types.length; i++) if (!(args[i] == null || isInstanceX(types[i], args[i]))) {
      if (debug)
        System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
      return false;
    }
    return true;
  }

  static public String concat(Object... l) {
    return concatStrings(l);
  }

  static public String concat(Iterable l) {
    return concatStrings(l);
  }

  static public Throwable getException(Runnable r) {
    try {
      callF(r);
      return null;
    } catch (Throwable e) {
      return e;
    }
  }

  static public RuntimeException fail() {
    throw new RuntimeException("fail");
  }

  static public RuntimeException fail(Throwable e) {
    throw asRuntimeException(e);
  }

  static public RuntimeException fail(Object msg) {
    throw new RuntimeException(String.valueOf(msg));
  }

  static public RuntimeException fail(Object... objects) {
    throw new Fail(objects);
  }

  static public RuntimeException fail(String msg) {
    throw new RuntimeException(msg == null ? "" : msg);
  }

  static public RuntimeException fail(String msg, Throwable innerException) {
    throw new RuntimeException(msg, innerException);
  }

  static public char[] chars(String s) {
    return asChars(s);
  }

  static public Class _getClass2(Object o) {
    return o == null ? null : o.getClass();
  }

  static public Duration daysToDuration(double days) {
    return msToDuration(daysToMS(days));
  }

  static public <A> A getVar(IF0<A> v) {
    return v == null ? null : v.get();
  }

  static public <A> A getVar(Optional<A> v) {
    return v == null ? null : v.orElse(null);
  }

  static public double[] litdoublearray(double... l) {
    return cloneDoubleArray(l);
  }

  static public BigInteger mul(BigInteger a, BigInteger b) {
    return a.multiply(b);
  }

  static public BigInteger mul(BigInteger a, long b) {
    return a.multiply(bigint(b));
  }

  static public int mul(int a, int b) {
    return a * b;
  }

  static public long mul(long a, long b) {
    return a * b;
  }

  static public double mul(double a, double b) {
    return a * b;
  }

  static public void nop() {
  }

  static public byte toByte(Object o) {
    if (o == null)
      return (byte) 0;
    if (o instanceof Number)
      return ((Number) o).byteValue();
    if (o instanceof String)
      return toByte(parseInt((String) o));
    throw fail("not convertible to byte: " + getClassName(o));
  }

  static public byte toByte(int i) {
    return (byte) i;
  }

  static public byte[] litbytearray(byte... l) {
    return cloneByteArray(l);
  }

  static public List<String> splitAtSpace(String s) {
    return empty(s) ? emptyList() : asList(s.split("\\s+"));
  }

  static public Pt neg(Pt p) {
    return p == null ? null : new Pt(-p.x, -p.y);
  }

  static public int neg(int i) {
    return -i;
  }

  static public double neg(double i) {
    return -i;
  }

  static public void sleepSeconds(double s) {
    if (s > 0)
      sleep(round(s * 1000));
  }

  static public float[] newFloatArray(int n) {
    return new float[n];
  }

  static public long nanoTime() {
    return System.nanoTime();
  }

  static public <A> List<A> reversed(Iterable<A> l) {
    return reversedList(l);
  }

  static public <A> List<A> reversed(A[] l) {
    return reversedList(asList(l));
  }

  static public String reversed(String s) {
    return reversedString(s);
  }

  static public <A> AutoCloseable tempAdd(Collection<A> l, A a) {
    if (l == null || l.contains(a))
      return null;
    l.add(a);
    return new tempAdd_undo(l, a);
  }

  static public class tempAdd_undo<A> implements AutoCloseable, IFieldsToList {

    public Collection<A> l;

    public A a;

    public tempAdd_undo() {
    }

    public tempAdd_undo(Collection<A> l, A a) {
      this.a = a;
      this.l = l;
    }

    public String toString() {
      return shortClassName_dropNumberPrefix(this) + "(" + l + ", " + a + ")";
    }

    public Object[] _fieldsToList() {
      return new Object[] { l, a };
    }

    public void close() {
      try {
        l.remove(a);
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }
  }

  static public AutoCloseable tempAdd(Container a, Component b) {
    if (a == null || b == null)
      return null;
    {
      return swing(() -> {
        if (b.getParent() == a)
          return null;
        a.add(b);
        return () -> {
          swing(() -> {
            if (b.getParent() == a)
              removeFromParent(b);
          });
        };
      });
    }
  }

  static public String concatStrings(Object... l) {
    return join("", l);
  }

  static public String concatStrings(Iterable l) {
    return join("", l);
  }

  static public BigInteger plus(BigInteger a, BigInteger b) {
    return a.add(b);
  }

  static public BigInteger plus(BigInteger a, long b) {
    return a.add(bigint(b));
  }

  static public long plus(long a, long b) {
    return a + b;
  }

  static public int plus(int a, int b) {
    return a + b;
  }

  static public float plus(float a, float b) {
    return a + b;
  }

  static public double plus(double a, double b) {
    return a + b;
  }

  static public <A> List<A> minus(Collection<A> a, Object... b) {
    Set set = asSet(b);
    List l = new ArrayList();
    for (Object s : unnull(a)) if (!set.contains(s))
      l.add(s);
    return l;
  }

  static public BigInteger minus(BigInteger a, BigInteger b) {
    return a.subtract(b);
  }

  static public Complex minus(Complex c) {
    return c == null ? null : complex(-c.re(), -c.im());
  }

  static public int minus(int a, int b) {
    return a - b;
  }

  static public int minus(int a) {
    return -a;
  }

  static public double minus(double a, double b) {
    return a - b;
  }

  static public double minus(double a) {
    return -a;
  }

  static public long minus(long a, long b) {
    return a - b;
  }

  static public long minus(long a) {
    return -a;
  }

  static public boolean lessThan(int a, int b) {
    return a < b;
  }

  static public boolean lessThan(double a, double b) {
    return a < b;
  }

  static public boolean lessThan(Object a, Object b) {
    return cmp(a, b) < 0;
  }

  static public boolean greaterThan(Object a, Object b) {
    return cmp(a, b) > 0;
  }

  static public boolean greaterThan(int a, int b) {
    return a > b;
  }

  static public boolean greaterThan(double a, double b) {
    return a > b;
  }

  static public BufferedImage newImage(int w, int h) {
    return whiteImage(w, h);
  }

  static public BufferedImage newImage(int w, Color color) {
    return newImage(w, w, color);
  }

  static public BufferedImage newImage(int w, int h, Color color) {
    return bufferedImage(w, h, color);
  }

  static public BufferedImage newImage(int size) {
    return whiteImage(size);
  }

  static public BufferedImage newImage(Pt size) {
    return whiteImage(size.x, size.y);
  }

  static public BufferedImage newImage(WidthAndHeight size) {
    return whiteImage(size.w(), size.h());
  }

  static public BufferedImage newImage(Dimension size) {
    return whiteImage(size.width, size.height);
  }

  static public Rect randomRect(Rect outer) {
    var w = random_incl(outer.w);
    var h = random_incl(outer.h);
    return randomRect(w, h, outer);
  }

  static public Rect randomRect(int w, int h, Rect outer) {
    return translateRect(outer.x, outer.y, randomRect(outer.w, outer.h, 0, w, h));
  }

  static public Rect randomRect(Rect outer, int w, int h) {
    return randomRect(w, h, outer);
  }

  static public Rect randomRect(int w, int h, int rw, int rh) {
    return randomRect(w, h, 0, rw, rh);
  }

  static public Rect randomRect(int w, int h, int border, int rw, int rh) {
    if (rw > w - border * 2 || rh > h - border * 2)
      return null;
    int ww = random(border, w - border - rw);
    int hh = random(border, h - border - rh);
    return new Rect(ww, hh, rw, rh);
  }

  static public Rect randomRect(int w, int h) {
    int rw = random(w);
    int rh = random(h);
    return randomRect(w, h, 0, rw, rh);
  }

  static public Rect randomRect() {
    return randomRect(currentImage().getWidth(), currentImage().getHeight());
  }

  static public Rect randomRect(BufferedImage img, int rw, int rh) {
    return randomRect(img.getWidth(), img.getHeight(), 0, rw, rh);
  }

  static public long longPlus(long a, long b) {
    return a + b;
  }

  static public int intSum(Collection c, String field) {
    int sum = 0;
    for (Object o : unnull(c)) sum += toInt(getOpt(o, field));
    return sum;
  }

  static public int intSum(Iterable<Integer> l) {
    int sum = 0;
    for (Integer i : unnull(l)) if (i != null)
      sum += i;
    return sum;
  }

  static public int intSum(int... l) {
    int sum = 0;
    for (int i : unnull(l)) sum += i;
    return sum;
  }

  static public List repF(int n, Object f, Object... args) {
    List l = emptyList(n);
    for (int i = 0; i < n; i++) l.add(callF(f, args));
    return l;
  }

  static public List repF(Object f, int n) {
    return repF(n, f);
  }

  static public <A> List<A> repF(int n, IF0<A> f) {
    List<A> l = emptyList(n);
    for (int i = 0; i < n; i++) l.add(f.get());
    return l;
  }

  static public <A> List<A> repF(IF0<A> f, int n) {
    return repF(n, f);
  }

  static public IterableIterator<Integer> iota(final int n) {
    return new IterableIterator<Integer>() {

      public int i = 0;

      public boolean hasNext() {
        return i < n;
      }

      public Integer next() {
        return ++i;
      }
    };
  }

  static public <A, C extends Collection<A>> C assertContains(C c, A y) {
    return assertContains(null, c, y);
  }

  static public <A, C extends Collection<A>> C assertContains(String msg, C c, A y) {
    if (!contains(c, y))
      throw fail((msg != null ? msg + ": " : "") + y + " not contained in " + c);
    return c;
  }

  static public String assertContains(String a, String b) {
    if (!contains(a, b))
      throw fail(quote(b) + " not contained in " + quote(a));
    return a;
  }

  static public void assertNotEquals(String msg, Object a, Object b) {
    if (eq(a, b))
      throw fail(msg);
  }

  static public void assertNotEquals(Object a, Object b) {
    if (eq(a, b))
      throw fail(str(a));
  }

  static public File userDir() {
    return new File(userHome());
  }

  static public File userDir(String path) {
    return new File(userHome(), path);
  }

  static public String f2s(File f) {
    return f == null ? null : f.getAbsolutePath();
  }

  static public String f2s(String s) {
    return f2s(newFile(s));
  }

  static public String f2s(java.nio.file.Path p) {
    return p == null ? null : f2s(p.toFile());
  }

  static public File newFile(File base, String... names) {
    for (String name : names) base = new File(base, name);
    return base;
  }

  static public File newFile(String name) {
    return name == null ? null : new File(name);
  }

  static public File newFile(String base, String... names) {
    return newFile(newFile(base), names);
  }

  static public Object leftArrow(String script) {
    GazelleV_LeftArrowScriptParser parser = new GazelleV_LeftArrowScriptParser();
    parser.allowTheWorld();
    return parser.parse(script).get();
  }

  static public Object leftArrow(GazelleV_LeftArrowScriptParser parser, String script) {
    return parser.parse(script).get();
  }

  static public <A> A innerExceptionOfType(Class<A> c, Throwable e) {
    while (true) {
      ping();
      if (e == null)
        return null;
      if (isInstanceOf(e, c))
        return (A) e;
      e = e.getCause();
    }
  }

  static public <A> A assertNotNullVerbose(A y) {
    assertNotNullVerbose((String) null, y);
    return y;
  }

  static public <A> A assertNotNullVerbose(String msg, A y) {
    if (y == null) {
      throw fail((msg != null ? msg + ": " : "") + "value is null");
    } else
      print("OK (not null): " + y);
    return y;
  }

  static public Method findMethod_cached(Object o, String method, Object... args) {
    try {
      if (o == null)
        return null;
      if (o instanceof Class) {
        _MethodCache cache = callOpt_getCache((Class) o);
        List<Method> methods = cache.cache.get(method);
        if (methods != null)
          for (Method m : methods) if (isStaticMethod(m) && findMethod_checkArgs(m, args, false))
            return m;
        return null;
      } else {
        _MethodCache cache = callOpt_getCache(o.getClass());
        List<Method> methods = cache.cache.get(method);
        if (methods != null)
          for (Method m : methods) if (findMethod_checkArgs(m, args, false))
            return m;
        return null;
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public boolean isInstanceX(Class type, Object arg) {
    if (type == boolean.class)
      return arg instanceof Boolean;
    if (type == int.class)
      return arg instanceof Integer;
    if (type == long.class)
      return arg instanceof Long;
    if (type == float.class)
      return arg instanceof Float;
    if (type == short.class)
      return arg instanceof Short;
    if (type == char.class)
      return arg instanceof Character;
    if (type == byte.class)
      return arg instanceof Byte;
    if (type == double.class)
      return arg instanceof Double;
    return type.isInstance(arg);
  }

  static public Map<Class, ArrayList<Method>> callF_cache = newDangerousWeakHashMap();

  static public <A> A callF(F0<A> f) {
    return f == null ? null : f.get();
  }

  static public <A, B> B callF(F1<A, B> f, A a) {
    return f == null ? null : f.get(a);
  }

  static public <A> A callF(IF0<A> f) {
    return f == null ? null : f.get();
  }

  static public <A, B> B callF(IF1<A, B> f, A a) {
    return f == null ? null : f.get(a);
  }

  static public <A, B> B callF(A a, IF1<A, B> f) {
    return f == null ? null : f.get(a);
  }

  static public <A, B, C> C callF(IF2<A, B, C> f, A a, B b) {
    return f == null ? null : f.get(a, b);
  }

  static public <A> void callF(VF1<A> f, A a) {
    if (f != null)
      f.get(a);
  }

  static public <A> void callF(A a, IVF1<A> f) {
    if (f != null)
      f.get(a);
  }

  static public <A> void callF(IVF1<A> f, A a) {
    if (f != null)
      f.get(a);
  }

  static public Object callF(Runnable r) {
    {
      if (r != null)
        r.run();
    }
    return null;
  }

  static public Object callF(Object f, Object... args) {
    return safeCallF(f, args);
  }

  static public Object safeCallF(Object f, Object... args) {
    if (f instanceof Runnable) {
      ((Runnable) f).run();
      return null;
    }
    if (f == null)
      return null;
    Class c = f.getClass();
    ArrayList<Method> methods;
    synchronized (callF_cache) {
      methods = callF_cache.get(c);
      if (methods == null)
        methods = callF_makeCache(c);
    }
    int n = l(methods);
    if (n == 0) {
      if (f instanceof String)
        throw fail("Legacy call: " + f);
      throw fail("No get method in " + getClassName(c));
    }
    if (n == 1)
      return invokeMethod(methods.get(0), f, args);
    for (int i = 0; i < n; i++) {
      Method m = methods.get(i);
      if (call_checkArgs(m, args, false))
        return invokeMethod(m, f, args);
    }
    throw fail("No matching get method in " + getClassName(c));
  }

  static public ArrayList<Method> callF_makeCache(Class c) {
    ArrayList<Method> l = new ArrayList();
    Class _c = c;
    do {
      for (Method m : _c.getDeclaredMethods()) if (m.getName().equals("get")) {
        makeAccessible(m);
        l.add(m);
      }
      if (!l.isEmpty())
        break;
      _c = _c.getSuperclass();
    } while (_c != null);
    callF_cache.put(c, l);
    return l;
  }

  static public RuntimeException asRuntimeException(Throwable t) {
    if (t instanceof Error)
      _handleError((Error) t);
    return t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t);
  }

  static public char[] asChars(String s) {
    return s == null ? null : s.toCharArray();
  }

  static public Duration msToDuration(double ms) {
    return Duration.ofNanos(lround(msToNanos(ms)));
  }

  static public long daysToMS(double days) {
    return round(days * 24 * 60 * 60 * 1000);
  }

  static public double[] cloneDoubleArray(double[] a) {
    if (a == null)
      return null;
    double[] b = new double[a.length];
    System.arraycopy(a, 0, b, 0, a.length);
    return b;
  }

  static public BigInteger bigint(String s) {
    return new BigInteger(s);
  }

  static public BigInteger bigint(long l) {
    return BigInteger.valueOf(l);
  }

  static public int parseInt(String s) {
    return emptyString(s) ? 0 : Integer.parseInt(s);
  }

  static public int parseInt(char c) {
    return Integer.parseInt(str(c));
  }

  static public String getClassName(Object o) {
    return o == null ? "null" : o instanceof Class ? ((Class) o).getName() : o.getClass().getName();
  }

  static public byte[] cloneByteArray(byte[] a) {
    if (a == null)
      return null;
    byte[] b = new byte[a.length];
    System.arraycopy(a, 0, b, 0, a.length);
    return b;
  }

  static public boolean empty(Collection c) {
    return c == null || c.isEmpty();
  }

  static public boolean empty(Iterable c) {
    return c == null || !c.iterator().hasNext();
  }

  static public boolean empty(CharSequence s) {
    return s == null || s.length() == 0;
  }

  static public boolean empty(Map map) {
    return map == null || map.isEmpty();
  }

  static public boolean empty(Object[] o) {
    return o == null || o.length == 0;
  }

  static public boolean empty(BitSet bs) {
    return bs == null || bs.isEmpty();
  }

  static public boolean empty(Object o) {
    if (o instanceof Collection)
      return empty((Collection) o);
    if (o instanceof String)
      return empty((String) o);
    if (o instanceof Map)
      return empty((Map) o);
    if (o instanceof Object[])
      return empty((Object[]) o);
    if (o instanceof byte[])
      return empty((byte[]) o);
    if (o == null)
      return true;
    throw fail("unknown type for 'empty': " + getType(o));
  }

  static public boolean empty(Iterator i) {
    return i == null || !i.hasNext();
  }

  static public boolean empty(double[] a) {
    return a == null || a.length == 0;
  }

  static public boolean empty(float[] a) {
    return a == null || a.length == 0;
  }

  static public boolean empty(int[] a) {
    return a == null || a.length == 0;
  }

  static public boolean empty(long[] a) {
    return a == null || a.length == 0;
  }

  static public boolean empty(byte[] a) {
    return a == null || a.length == 0;
  }

  static public boolean empty(short[] a) {
    return a == null || a.length == 0;
  }

  static public boolean empty(MultiSet ms) {
    return ms == null || ms.isEmpty();
  }

  static public boolean empty(IMultiMap mm) {
    return mm == null || mm.size() == 0;
  }

  static public boolean empty(File f) {
    return getFileSize(f) == 0;
  }

  static public boolean empty(IntRange r) {
    return r == null || r.empty();
  }

  static public boolean empty(Rect r) {
    return !(r != null && r.w != 0 && r.h != 0);
  }

  static public boolean empty(Chain c) {
    return c == null;
  }

  static public boolean empty(AppendableChain c) {
    return c == null;
  }

  static public boolean empty(IntSize l) {
    return l == null || l.size() == 0;
  }

  static public ArrayList emptyList() {
    return new ArrayList();
  }

  static public ArrayList emptyList(int capacity) {
    return new ArrayList(max(0, capacity));
  }

  static public ArrayList emptyList(Iterable l) {
    return l instanceof Collection ? emptyList(((Collection) l).size()) : emptyList();
  }

  static public ArrayList emptyList(Object[] l) {
    return emptyList(l(l));
  }

  static public <A> ArrayList<A> emptyList(Class<A> c) {
    return new ArrayList();
  }

  static public <A> ArrayList<A> asList(A[] a) {
    return a == null ? new ArrayList<A>() : new ArrayList<A>(Arrays.asList(a));
  }

  static public ArrayList<Character> asList(char[] a) {
    if (a == null)
      return null;
    ArrayList<Character> l = emptyList(a.length);
    for (var i : a) l.add(i);
    return l;
  }

  static public ArrayList<Byte> asList(byte[] a) {
    if (a == null)
      return null;
    ArrayList<Byte> l = emptyList(a.length);
    for (var i : a) l.add(i);
    return l;
  }

  static public ArrayList<Integer> asList(int[] a) {
    if (a == null)
      return null;
    ArrayList<Integer> l = emptyList(a.length);
    for (int i : a) l.add(i);
    return l;
  }

  static public ArrayList<Long> asList(long[] a) {
    if (a == null)
      return null;
    ArrayList<Long> l = emptyList(a.length);
    for (long i : a) l.add(i);
    return l;
  }

  static public ArrayList<Float> asList(float[] a) {
    if (a == null)
      return null;
    ArrayList<Float> l = emptyList(a.length);
    for (float i : a) l.add(i);
    return l;
  }

  static public ArrayList<Double> asList(double[] a) {
    if (a == null)
      return null;
    ArrayList<Double> l = emptyList(a.length);
    for (double i : a) l.add(i);
    return l;
  }

  static public ArrayList<Short> asList(short[] a) {
    if (a == null)
      return null;
    ArrayList<Short> l = emptyList(a.length);
    for (short i : a) l.add(i);
    return l;
  }

  static public <A> ArrayList<A> asList(Iterator<A> it) {
    ArrayList l = new ArrayList();
    if (it != null)
      while (it.hasNext()) l.add(it.next());
    return l;
  }

  static public <A> ArrayList<A> asList(IterableIterator<A> s) {
    return asList((Iterator) s);
  }

  static public <A> ArrayList<A> asList(Iterable<A> s) {
    if (s instanceof ArrayList)
      return (ArrayList) s;
    ArrayList l = new ArrayList();
    if (s != null)
      for (A a : s) l.add(a);
    return l;
  }

  static public <A> ArrayList<A> asList(Producer<A> p) {
    ArrayList l = new ArrayList();
    A a;
    if (p != null)
      while ((a = p.next()) != null) l.add(a);
    return l;
  }

  static public <A> ArrayList<A> asList(Enumeration<A> e) {
    ArrayList l = new ArrayList();
    if (e != null)
      while (e.hasMoreElements()) l.add(e.nextElement());
    return l;
  }

  static public <A> ArrayList<A> asList(ReverseChain<A> c) {
    return c == null ? emptyList() : c.toList();
  }

  static public <A> List<A> asList(Pair<A, A> p) {
    return p == null ? null : ll(p.a, p.b);
  }

  static volatile public boolean sleep_noSleep = false;

  static public void sleep(long ms) {
    ping();
    if (ms < 0)
      return;
    if (isAWTThread() && ms > 100)
      throw fail("Should not sleep on AWT thread");
    try {
      Thread.sleep(ms);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  static public void sleep() {
    try {
      if (sleep_noSleep)
        throw fail("nosleep");
      print("Sleeping.");
      sleepQuietly();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public long round(double d) {
    return Math.round(d);
  }

  static public String round(String s) {
    return roundBracket(s);
  }

  static public Complex round(Complex c) {
    return new Complex(round(c.re), round(c.im));
  }

  static public <A> List<A> reversedList(Iterable<A> l) {
    List<A> x = cloneList(l);
    Collections.reverse(x);
    return x;
  }

  static public String reversedString(String s) {
    return reverseString(s);
  }

  static public String shortClassName_dropNumberPrefix(Object o) {
    return dropNumberPrefix(shortClassName(o));
  }

  static public RuntimeException rethrow(Throwable t) {
    if (t instanceof Error)
      _handleError((Error) t);
    throw t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t);
  }

  static public RuntimeException rethrow(String msg, Throwable t) {
    throw new RuntimeException(msg, t);
  }

  static public Object swing(Object f) {
    return swingAndWait(f);
  }

  static public void swing(Runnable f) {
    swingAndWait(f);
  }

  static public <A> A swing(F0<A> f) {
    return (A) swingAndWait(f);
  }

  static public <A> A swing(IF0<A> f) {
    return (A) swingAndWait(f);
  }

  static public void removeFromParent(final Component c) {
    if (c != null) {
      swing(() -> {
        Container cc = c.getParent();
        if (cc != null) {
          cc.remove(c);
          revalidate(cc);
        }
      });
    }
  }

  public static <A> String join(String glue, Iterable<A> strings) {
    if (strings == null)
      return "";
    if (strings instanceof Collection) {
      if (((Collection) strings).size() == 1)
        return strOrEmpty(first((Collection) strings));
    }
    StringBuilder buf = new StringBuilder();
    Iterator<A> i = strings.iterator();
    if (i.hasNext()) {
      buf.append(strOrEmpty(i.next()));
      while (i.hasNext()) buf.append(glue).append(strOrEmpty(i.next()));
    }
    return buf.toString();
  }

  public static String join(String glue, String... strings) {
    return join(glue, Arrays.asList(strings));
  }

  public static String join(String glue, Object... strings) {
    return join(glue, Arrays.asList(strings));
  }

  static public <A> String join(Iterable<A> strings) {
    return join("", strings);
  }

  static public <A> String join(Iterable<A> strings, String glue) {
    return join(glue, strings);
  }

  public static String join(String[] strings) {
    return join("", strings);
  }

  static public String join(String glue, Pair p) {
    return p == null ? "" : str(p.a) + glue + str(p.b);
  }

  static public Set asSet(Object[] array) {
    HashSet set = new HashSet();
    for (Object o : array) if (o != null)
      set.add(o);
    return set;
  }

  static public Set<String> asSet(String[] array) {
    TreeSet<String> set = new TreeSet();
    for (String o : array) if (o != null)
      set.add(o);
    return set;
  }

  static public <A> Set<A> asSet(Iterable<A> l) {
    if (l instanceof Set)
      return (Set) l;
    HashSet<A> set = new HashSet();
    for (A o : unnull(l)) if (o != null)
      set.add(o);
    return set;
  }

  static public <A> Set<A> asSet(MultiSet<A> ms) {
    return ms == null ? null : ms.asSet();
  }

  static public String unnull(String s) {
    return s == null ? "" : s;
  }

  static public <A> Collection<A> unnull(Collection<A> l) {
    return l == null ? emptyList() : l;
  }

  static public <A> List<A> unnull(List<A> l) {
    return l == null ? emptyList() : l;
  }

  static public int[] unnull(int[] l) {
    return l == null ? emptyIntArray() : l;
  }

  static public char[] unnull(char[] l) {
    return l == null ? emptyCharArray() : l;
  }

  static public double[] unnull(double[] l) {
    return l == null ? emptyDoubleArray() : l;
  }

  static public float[] unnull(float[] l) {
    return l == null ? emptyFloatArray() : l;
  }

  static public <A, B> Map<A, B> unnull(Map<A, B> l) {
    return l == null ? emptyMap() : l;
  }

  static public <A> Iterable<A> unnull(Iterable<A> i) {
    return i == null ? emptyList() : i;
  }

  static public <A> A[] unnull(A[] a) {
    return a == null ? (A[]) emptyObjectArray() : a;
  }

  static public BitSet unnull(BitSet b) {
    return b == null ? new BitSet() : b;
  }

  static public Pt unnull(Pt p) {
    return p == null ? new Pt() : p;
  }

  static public Symbol unnull(Symbol s) {
    return s == null ? emptySymbol() : s;
  }

  static public <A, B> Pair<A, B> unnull(Pair<A, B> p) {
    return p != null ? p : new Pair(null, null);
  }

  static public int unnull(Integer i) {
    return i == null ? 0 : i;
  }

  static public long unnull(Long l) {
    return l == null ? 0L : l;
  }

  static public double unnull(Double l) {
    return l == null ? 0.0 : l;
  }

  static public Complex complex(double re, double im) {
    return new Complex(re, im);
  }

  static public Complex complex(double re) {
    return new Complex(re, 0.0);
  }

  static public Complex complex(double[] reIm) {
    if (empty(reIm))
      return null;
    if (l(reIm) != 2)
      throw fail("Need 2 doubles to make complex number");
    return complex(reIm[0], reIm[1]);
  }

  static public int cmp(Number a, Number b) {
    return a == null ? b == null ? 0 : -1 : cmp(a.doubleValue(), b.doubleValue());
  }

  static public int cmp(double a, double b) {
    return a < b ? -1 : a == b ? 0 : 1;
  }

  static public int cmp(int a, int b) {
    return a < b ? -1 : a == b ? 0 : 1;
  }

  static public int cmp(long a, long b) {
    return a < b ? -1 : a == b ? 0 : 1;
  }

  static public int cmp(Object a, Object b) {
    if (a == null)
      return b == null ? 0 : -1;
    if (b == null)
      return 1;
    return ((Comparable) a).compareTo(b);
  }

  static public BufferedImage whiteImage(int w, int h) {
    return newBufferedImage(w, h, Color.white);
  }

  static public BufferedImage whiteImage(int size) {
    return whiteImage(size, size);
  }

  static public BufferedImage whiteImage(WidthAndHeight size) {
    return whiteImage(size.getWidth(), size.getHeight());
  }

  static public BufferedImage bufferedImage(int[] pixels, int w, int h) {
    return intArrayToBufferedImage(pixels, w, h);
  }

  static public BufferedImage bufferedImage(int[] pixels, WidthAndHeight size) {
    return bufferedImage(pixels, size.getWidth(), size.getHeight());
  }

  static public BufferedImage bufferedImage(int w, int h, int[] pixels) {
    return intArrayToBufferedImage(pixels, w, h);
  }

  static public BufferedImage bufferedImage(int w, int h) {
    return newBufferedImage(w, h);
  }

  static public BufferedImage bufferedImage(int w, int h, RGB rgb) {
    return newBufferedImage(w, h, rgb);
  }

  static public BufferedImage bufferedImage(int w, Color color) {
    return bufferedImage(w, w, color);
  }

  static public BufferedImage bufferedImage(int w, int h, Color color) {
    return newBufferedImage(w, h, color);
  }

  static public BufferedImage bufferedImage(Pt p, Color color) {
    return newBufferedImage(p, color);
  }

  static public BufferedImage bufferedImage(WidthAndHeight size, Color color) {
    return newBufferedImage(size.w(), size.h(), color);
  }

  static public BufferedImage bufferedImage(Color color, WidthAndHeight size) {
    return bufferedImage(size, color);
  }

  static public int random_incl(int min, int max) {
    return random_incl(min, max, defaultRandomizer());
  }

  static public int random_incl(int min, int max, Random random) {
    return random(min, max + 1, random);
  }

  static public int random_incl(int max) {
    return random(0, max + 1);
  }

  static public Rect translateRect(int x, int y, Rect r) {
    return translateRect(r, x, y);
  }

  static public Rect translateRect(Rect r, int x, int y) {
    return r == null ? null : new Rect(r.x + x, r.y + y, r.w, r.h);
  }

  static public int random(int n) {
    return random(n, defaultRandomGenerator());
  }

  static public int random(int n, Random r) {
    return random(r, n);
  }

  static public long random(long n) {
    return random(n, defaultRandomGenerator());
  }

  static public long random(long n, Random r) {
    return random(r, n);
  }

  static public int random(Random r, int n) {
    return n <= 0 ? 0 : getRandomizer(r).nextInt(n);
  }

  static public long random(Random r, long n) {
    return n <= 0 ? 0 : getRandomizer(r).nextLong(n);
  }

  static public double random(double max) {
    return random() * max;
  }

  static public double random() {
    return defaultRandomGenerator().nextInt(100001) / 100000.0;
  }

  static public double random(double min, double max) {
    return min + random() * (max - min);
  }

  static public int random(int min, int max) {
    return min + random(max - min);
  }

  static public long random(long min, long max) {
    return min + random(max - min);
  }

  static public int random(int min, int max, Random r) {
    return random(r, min, max);
  }

  static public int random(Random r, int min, int max) {
    return min + random(r, max - min);
  }

  static public <A> A random(List<A> l) {
    return oneOf(l);
  }

  static public <A> A random(Collection<A> c) {
    if (c instanceof List)
      return random((List<A>) c);
    int i = random(l(c));
    return collectionGet(c, i);
  }

  static public int random(IntRange r) {
    return random(r.start, r.end);
  }

  static public <A, B> Pair<A, B> random(Map<A, B> map) {
    return entryToPair(random(entries(map)));
  }

  static public ThreadLocal<BufferedImage> currentImage_var = new ThreadLocal();

  static public BufferedImage currentImage() {
    return currentImage_var.get();
  }

  static public void currentImage(BufferedImage img) {
    currentImage_var.set(img);
  }

  static public int toInt(Object o) {
    if (o == null)
      return 0;
    if (o instanceof Number)
      return ((Number) o).intValue();
    if (o instanceof String)
      return parseInt((String) o);
    if (o instanceof Boolean)
      return boolToInt((Boolean) o);
    throw fail("woot not int: " + getClassName(o));
  }

  static public int toInt(long l) {
    if (l != (int) l)
      throw fail("Too large for int: " + l);
    return (int) l;
  }

  static public Object getOpt(Object o, String field) {
    return getOpt_cached(o, field);
  }

  static public Object getOpt(String field, Object o) {
    return getOpt_cached(o, field);
  }

  static public Object getOpt_raw(Object o, String field) {
    try {
      Field f = getOpt_findField(o.getClass(), field);
      if (f == null)
        return null;
      makeAccessible(f);
      return f.get(o);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Object getOpt(Class c, String field) {
    try {
      if (c == null)
        return null;
      Field f = getOpt_findStaticField(c, field);
      if (f == null)
        return null;
      makeAccessible(f);
      return f.get(null);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Field getOpt_findStaticField(Class<?> c, String field) {
    Class _c = c;
    do {
      for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0)
        return f;
      _c = _c.getSuperclass();
    } while (_c != null);
    return null;
  }

  static public boolean contains(Collection c, Object o) {
    return c != null && c.contains(o);
  }

  static public boolean contains(Iterable it, Object a) {
    if (it != null)
      for (Object o : it) if (eq(a, o))
        return true;
    return false;
  }

  static public boolean contains(Object[] x, Object o) {
    if (x != null)
      for (Object a : x) if (eq(a, o))
        return true;
    return false;
  }

  static public boolean contains(String s, char c) {
    return s != null && s.indexOf(c) >= 0;
  }

  static public boolean contains(String s, String b) {
    return s != null && s.indexOf(b) >= 0;
  }

  static public boolean contains(BitSet bs, int i) {
    return bs != null && bs.get(i);
  }

  static public <A> boolean contains(Producer<A> p, A a) {
    if (p != null && a != null)
      while (true) {
        A x = p.next();
        if (x == null)
          break;
        if (eq(x, a))
          return true;
      }
    return false;
  }

  static public boolean contains(Rect r, Pt p) {
    return rectContains(r, p);
  }

  static public String quote(Object o) {
    if (o == null)
      return "null";
    return quote(str(o));
  }

  static public String quote(String s) {
    if (s == null)
      return "null";
    StringBuilder out = new StringBuilder((int) (l(s) * 1.5 + 2));
    quote_impl(s, out);
    return out.toString();
  }

  static public void quote_impl(String s, StringBuilder out) {
    out.append('"');
    int l = s.length();
    for (int i = 0; i < l; i++) {
      char c = s.charAt(i);
      if (c == '\\' || c == '"')
        out.append('\\').append(c);
      else if (c == '\r')
        out.append("\\r");
      else if (c == '\n')
        out.append("\\n");
      else if (c == '\t')
        out.append("\\t");
      else if (c == '\0')
        out.append("\\0");
      else
        out.append(c);
    }
    out.append('"');
  }

  static public boolean eq(Object a, Object b) {
    return a == b || a != null && b != null && a.equals(b);
  }

  static public boolean eq(Symbol a, String b) {
    return eq(str(a), b);
  }

  static public String str(Object o) {
    return o == null ? "null" : o.toString();
  }

  static public String str(char[] c) {
    return c == null ? "null" : new String(c);
  }

  static public String str(char[] c, int offset, int count) {
    return new String(c, offset, count);
  }

  static public String _userHome;

  static public String userHome() {
    if (_userHome == null)
      return actualUserHome();
    return _userHome;
  }

  static public File userHome(String path) {
    return new File(userDir(), path);
  }

  static volatile public boolean ping_pauseAll = false;

  static public int ping_sleep = 100;

  static volatile public boolean ping_anyActions = false;

  static public Map<Thread, Object> ping_actions = newWeakHashMap();

  static public ThreadLocal<Boolean> ping_isCleanUpThread = new ThreadLocal();

  static public boolean ping(PingSource pingSource) {
    return ping();
  }

  static public boolean ping() {
    newPing();
    if (ping_pauseAll || ping_anyActions)
      ping_impl(true);
    return true;
  }

  static public boolean ping_impl(boolean okInCleanUp) {
    try {
      if (ping_pauseAll && !isAWTThread()) {
        do Thread.sleep(ping_sleep); while (ping_pauseAll);
        return true;
      }
      if (ping_anyActions) {
        if (!okInCleanUp && !isTrue(ping_isCleanUpThread.get()))
          failIfUnlicensed();
        Object action = null;
        synchronized (ping_actions) {
          if (!ping_actions.isEmpty()) {
            action = ping_actions.get(currentThread());
            if (action instanceof Runnable)
              ping_actions.remove(currentThread());
            if (ping_actions.isEmpty())
              ping_anyActions = false;
          }
        }
        if (action instanceof Runnable)
          ((Runnable) action).run();
        else if (eq(action, "cancelled"))
          throw fail("Thread cancelled.");
      }
      return false;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public boolean isInstanceOf(Object o, Class type) {
    return type.isInstance(o);
  }

  static volatile public StringBuffer local_log = new StringBuffer();

  static public boolean printAlsoToSystemOut = true;

  static volatile public Appendable print_log = local_log;

  static volatile public int print_log_max = 1024 * 1024;

  static volatile public int local_log_max = 100 * 1024;

  static public boolean print_silent = false;

  static public Object print_byThread_lock = new Object();

  static volatile public ThreadLocal<Object> print_byThread;

  static volatile public Object print_allThreads;

  static volatile public Object print_preprocess;

  static public void print() {
    print("");
  }

  static public <A> A print(String s, A o) {
    print(combinePrintParameters(s, o));
    return o;
  }

  static public <A> A print(A o) {
    ping_okInCleanUp();
    if (print_silent)
      return o;
    String s = o + "\n";
    print_noNewLine(s);
    return o;
  }

  static public void print_noNewLine(String s) {
    try {
      Object f = getThreadLocal(print_byThread_dontCreate());
      if (f == null)
        f = print_allThreads;
      if (f != null)
        if (isFalse(f instanceof F1 ? ((F1) f).get(s) : callF(f, s)))
          return;
    } catch (Throwable e) {
      System.out.println(getStackTrace(e));
    }
    print_raw(s);
  }

  static public void print_raw(String s) {
    if (print_preprocess != null)
      s = (String) callF(print_preprocess, s);
    s = fixNewLines(s);
    Appendable loc = local_log;
    Appendable buf = print_log;
    int loc_max = print_log_max;
    if (buf != loc && buf != null) {
      print_append(buf, s, print_log_max);
      loc_max = local_log_max;
    }
    if (loc != null)
      print_append(loc, s, loc_max);
    if (printAlsoToSystemOut)
      System.out.print(s);
    vmBus_send("printed", mc(), s);
  }

  static public void print_autoRotate() {
  }

  static public AutoCloseable tempInterceptPrintIfNotIntercepted(F1<String, Boolean> f) {
    return print_byThread().get() == null ? tempInterceptPrint(f) : null;
  }

  static final public Map<Class, _MethodCache> callOpt_cache = newDangerousWeakHashMap();

  static public Object callOpt_cached(Object o, String methodName, Object... args) {
    try {
      if (o == null)
        return null;
      if (o instanceof Class) {
        Class c = (Class) o;
        _MethodCache cache = callOpt_getCache(c);
        Method me = cache.findMethod(methodName, args);
        if (me == null || (me.getModifiers() & Modifier.STATIC) == 0)
          return null;
        return invokeMethod(me, null, args);
      } else {
        Class c = o.getClass();
        _MethodCache cache = callOpt_getCache(c);
        Method me = cache.findMethod(methodName, args);
        if (me == null)
          return null;
        return invokeMethod(me, o, args);
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public _MethodCache callOpt_getCache(Class c) {
    _MethodCache cache = callOpt_cache.get(c);
    if (cache == null)
      callOpt_cache.put(c, cache = new _MethodCache(c));
    return cache;
  }

  static public boolean isStaticMethod(Method m) {
    return methodIsStatic(m);
  }

  static public <A, B> Map<A, B> newDangerousWeakHashMap() {
    return _registerDangerousWeakMap(synchroMap(new WeakHashMap()));
  }

  static public <A, B> Map<A, B> newDangerousWeakHashMap(Object initFunction) {
    return _registerDangerousWeakMap(synchroMap(new WeakHashMap()), initFunction);
  }

  static public int l(Object[] a) {
    return a == null ? 0 : a.length;
  }

  static public int l(boolean[] a) {
    return a == null ? 0 : a.length;
  }

  static public int l(byte[] a) {
    return a == null ? 0 : a.length;
  }

  static public int l(short[] a) {
    return a == null ? 0 : a.length;
  }

  static public int l(long[] a) {
    return a == null ? 0 : a.length;
  }

  static public int l(int[] a) {
    return a == null ? 0 : a.length;
  }

  static public int l(float[] a) {
    return a == null ? 0 : a.length;
  }

  static public int l(double[] a) {
    return a == null ? 0 : a.length;
  }

  static public int l(char[] a) {
    return a == null ? 0 : a.length;
  }

  static public int l(Collection c) {
    return c == null ? 0 : c.size();
  }

  static public int l(Iterator i) {
    return iteratorCount_int_close(i);
  }

  static public int l(Map m) {
    return m == null ? 0 : m.size();
  }

  static public int l(CharSequence s) {
    return s == null ? 0 : s.length();
  }

  static public long l(File f) {
    return f == null ? 0 : f.length();
  }

  static public int l(IMultiSet ms) {
    return ms == null ? 0 : ms.size();
  }

  static public int l(IMultiMap mm) {
    return mm == null ? 0 : mm.size();
  }

  static public int l(IntRange r) {
    return r == null ? 0 : r.length();
  }

  static public int l(IntSize o) {
    return o == null ? 0 : o.size();
  }

  static public Object invokeMethod(Method m, Object o, Object... args) {
    try {
      try {
        return m.invoke(o, args);
      } catch (InvocationTargetException e) {
        throw rethrow(getExceptionCause(e));
      } catch (IllegalArgumentException e) {
        throw new IllegalArgumentException(e.getMessage() + " - was calling: " + m + ", args: " + joinWithSpace(classNames(args)));
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public boolean call_checkArgs(Method m, Object[] args, boolean debug) {
    Class<?>[] types = m.getParameterTypes();
    if (types.length != l(args)) {
      if (debug)
        print("Bad parameter length: " + args.length + " vs " + types.length);
      return false;
    }
    for (int i = 0; i < types.length; i++) {
      Object arg = args[i];
      if (!(arg == null ? !types[i].isPrimitive() : isInstanceX(types[i], arg))) {
        if (debug)
          print("Bad parameter " + i + ": " + arg + " vs " + types[i]);
        return false;
      }
    }
    return true;
  }

  static public Field makeAccessible(Field f) {
    try {
      f.setAccessible(true);
    } catch (Throwable e) {
      vmBus_send("makeAccessible_error", e, f);
    }
    return f;
  }

  static public Method makeAccessible(Method m) {
    try {
      m.setAccessible(true);
    } catch (Throwable e) {
      vmBus_send("makeAccessible_error", e, m);
    }
    return m;
  }

  static public Constructor makeAccessible(Constructor c) {
    try {
      c.setAccessible(true);
    } catch (Throwable e) {
      vmBus_send("makeAccessible_error", e, c);
    }
    return c;
  }

  static public void _handleError(Error e) {
  }

  static public long lround(double d) {
    return Math.round(d);
  }

  static public long lround(Number n) {
    return lround(toDouble(n));
  }

  static public double msToNanos(double ms) {
    return ms * 1e6;
  }

  static public boolean emptyString(String s) {
    return s == null || s.length() == 0;
  }

  static public String getType(Object o) {
    return getClassName(o);
  }

  static public long getFileSize(String path) {
    return path == null ? 0 : new File(path).length();
  }

  static public long getFileSize(File f) {
    return f == null ? 0 : f.length();
  }

  static public int max(int a, int b) {
    return Math.max(a, b);
  }

  static public int max(int a, int b, int c) {
    return max(max(a, b), c);
  }

  static public long max(int a, long b) {
    return Math.max((long) a, b);
  }

  static public long max(long a, long b) {
    return Math.max(a, b);
  }

  static public double max(int a, double b) {
    return Math.max((double) a, b);
  }

  static public float max(float a, float b) {
    return Math.max(a, b);
  }

  static public double max(double a, double b) {
    return Math.max(a, b);
  }

  static public <A extends Comparable<A>> A max(Iterable<A> l) {
    A max = null;
    var it = iterator(l);
    if (it.hasNext()) {
      max = it.next();
      while (it.hasNext()) {
        A a = it.next();
        if (cmp(a, max) > 0)
          max = a;
      }
    }
    return max;
  }

  static public double max(double[] c) {
    if (c.length == 0)
      return Double.MIN_VALUE;
    double x = c[0];
    for (int i = 1; i < c.length; i++) x = Math.max(x, c[i]);
    return x;
  }

  static public float max(float[] c) {
    if (c.length == 0)
      return Float.MAX_VALUE;
    float x = c[0];
    for (int i = 1; i < c.length; i++) x = Math.max(x, c[i]);
    return x;
  }

  static public byte max(byte[] c) {
    byte x = -128;
    for (byte d : c) if (d > x)
      x = d;
    return x;
  }

  static public short max(short[] c) {
    short x = -0x8000;
    for (short d : c) if (d > x)
      x = d;
    return x;
  }

  static public int max(int[] c) {
    int x = Integer.MIN_VALUE;
    for (int d : c) if (d > x)
      x = d;
    return x;
  }

  static public <A extends Comparable<A>> A max(A a, A b) {
    return cmp(a, b) >= 0 ? a : b;
  }

  static public <A> List<A> ll(A... a) {
    ArrayList l = new ArrayList(a.length);
    if (a != null)
      for (A x : a) l.add(x);
    return l;
  }

  static public boolean isAWTThread() {
    if (isAndroid())
      return false;
    if (isHeadless())
      return false;
    return isAWTThread_awt();
  }

  static public boolean isAWTThread_awt() {
    return SwingUtilities.isEventDispatchThread();
  }

  static public Object sleepQuietly_monitor = new Object();

  static public void sleepQuietly() {
    try {
      assertFalse(isAWTThread());
      synchronized (sleepQuietly_monitor) {
        sleepQuietly_monitor.wait();
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public String roundBracket(String s) {
    return "(" + s + ")";
  }

  static public String roundBracket(Object s) {
    return roundBracket(str(s));
  }

  static public <A> ArrayList<A> cloneList(Iterable<A> l) {
    return l instanceof Collection ? cloneList((Collection) l) : asList(l);
  }

  static public <A> ArrayList<A> cloneList(Collection<A> l) {
    if (l == null)
      return new ArrayList();
    synchronized (collectionMutex(l)) {
      return new ArrayList<A>(l);
    }
  }

  static public String reverseString(String s) {
    return empty(s) ? s : new StringBuilder(s).reverse().toString();
  }

  static public String dropNumberPrefix(String s) {
    return dropFirst(s, indexOfNonDigit(s));
  }

  static public String shortClassName(Object o) {
    if (o == null)
      return null;
    Class c = o instanceof Class ? (Class) o : o.getClass();
    String name = c.getName();
    return shortenClassName(name);
  }

  static public void swingAndWait(Runnable r) {
    try {
      if (isAWTThread())
        r.run();
      else {
        r = addThreadInfoToRunnable(r);
        executingSwingCode(r);
        EventQueue.invokeAndWait(r);
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Object swingAndWait(Object f) {
    if (isAWTThread())
      return callF(f);
    else {
      Var result = new Var();
      swingAndWait(new Runnable() {

        public void run() {
          try {
            result.set(callF(f));
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "result.set(callF(f));";
        }
      });
      return result.get();
    }
  }

  static public <A extends Component> A revalidate(final A c) {
    if (c == null || !c.isShowing())
      return c;
    {
      swing(() -> {
        c.revalidate();
        c.repaint();
      });
    }
    return c;
  }

  static public void revalidate(JFrame f) {
    revalidate((Component) f);
  }

  static public void revalidate(JInternalFrame f) {
    revalidate((Component) f);
  }

  static public String strOrEmpty(Object o) {
    return o == null ? "" : str(o);
  }

  static public Object first(Object list) {
    return first((Iterable) list);
  }

  static public <A> A first(List<A> list) {
    return empty(list) ? null : list.get(0);
  }

  static public <A> A first(A[] bla) {
    return bla == null || bla.length == 0 ? null : bla[0];
  }

  static public <A, B> Pair<A, B> first(Map<A, B> map) {
    return mapEntryToPair(first(entrySet(map)));
  }

  static public <A, B> Pair<A, B> first(MultiMap<A, B> mm) {
    if (mm == null)
      return null;
    var e = first(mm.data.entrySet());
    if (e == null)
      return null;
    return pair(e.getKey(), first(e.getValue()));
  }

  static public <A> A first(IterableIterator<A> i) {
    return first((Iterator<A>) i);
  }

  static public <A> A first(Iterator<A> i) {
    return i == null || !i.hasNext() ? null : i.next();
  }

  static public <A> A first(Iterable<A> i) {
    if (i == null)
      return null;
    Iterator<A> it = i.iterator();
    return it.hasNext() ? it.next() : null;
  }

  static public Character first(String s) {
    return empty(s) ? null : s.charAt(0);
  }

  static public Character first(CharSequence s) {
    return empty(s) ? null : s.charAt(0);
  }

  static public <A, B> A first(Pair<A, B> p) {
    return p == null ? null : p.a;
  }

  static public <A, B, C> A first(T3<A, B, C> t) {
    return t == null ? null : t.a;
  }

  static public Byte first(byte[] l) {
    return empty(l) ? null : l[0];
  }

  static public Double first(double[] l) {
    return empty(l) ? null : l[0];
  }

  static public <A> A first(A[] l, IF1<A, Boolean> pred) {
    return firstThat(l, pred);
  }

  static public <A> A first(Iterable<A> l, IF1<A, Boolean> pred) {
    return firstThat(l, pred);
  }

  static public <A> A first(IF1<A, Boolean> pred, Iterable<A> l) {
    return firstThat(pred, l);
  }

  static public <A> A first(AppendableChain<A> a) {
    return a == null ? null : a.element;
  }

  static public <A> A first(IMultiSet<A> a) {
    return a == null ? null : first(a.keySet());
  }

  static public int[] emptyIntArray_a = new int[0];

  static public int[] emptyIntArray() {
    return emptyIntArray_a;
  }

  static public char[] emptyCharArray = new char[0];

  static public char[] emptyCharArray() {
    return emptyCharArray;
  }

  static public double[] emptyDoubleArray = new double[0];

  static public double[] emptyDoubleArray() {
    return emptyDoubleArray;
  }

  static public float[] emptyFloatArray = new float[0];

  static public float[] emptyFloatArray() {
    return emptyFloatArray;
  }

  static public Map emptyMap() {
    return new HashMap();
  }

  static public Object[] emptyObjectArray_a = new Object[0];

  static public Object[] emptyObjectArray() {
    return emptyObjectArray_a;
  }

  static public Symbol emptySymbol_value;

  static public Symbol emptySymbol() {
    if (emptySymbol_value == null)
      emptySymbol_value = symbol("");
    return emptySymbol_value;
  }

  static public BufferedImage newBufferedImage(int w, int h) {
    return new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
  }

  static public BufferedImage newBufferedImage(int w, int h, RGB rgb) {
    return newBufferedImage(w, h, rgb.getColor());
  }

  static public BufferedImage newBufferedImage(int w, int h, Color color) {
    BufferedImage img = newBufferedImage(w, h);
    Graphics2D g = img.createGraphics();
    g.setColor(or(color, Color.white));
    g.fillRect(0, 0, w, h);
    return img;
  }

  static public BufferedImage newBufferedImage(Pt p, Color color) {
    return newBufferedImage(p.x, p.y, color);
  }

  static public BufferedImage newBufferedImage(WidthAndHeight size, Color color) {
    return newBufferedImage(size.w(), size.h(), color);
  }

  static public BufferedImage newBufferedImage(int w, int h, int[] pixels) {
    return intArrayToBufferedImage(pixels, w, h);
  }

  static public BufferedImageWithMeta intArrayToBufferedImage(int[] pixels, int w) {
    return intArrayToBufferedImage(pixels, w, pixels.length / w);
  }

  static public BufferedImageWithMeta intArrayToBufferedImage(int[] pixels, int w, int h) {
    if (w == 0 || h == 0)
      return null;
    int[] bitMasks = new int[] { 0xFF0000, 0xFF00, 0xFF, 0xFF000000 };
    SinglePixelPackedSampleModel sm = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, w, h, bitMasks);
    DataBufferInt db = new DataBufferInt(pixels, pixels.length);
    WritableRaster wr = Raster.createWritableRaster(sm, db, new Point());
    return new BufferedImageWithMeta(ColorModel.getRGBdefault(), wr, false, null);
  }

  static public BufferedImageWithMeta intArrayToBufferedImage(int w, int... pixels) {
    return intArrayToBufferedImage(pixels, w);
  }

  static public Random defaultRandomizer() {
    return defaultRandomGenerator();
  }

  static public Random defaultRandomGenerator() {
    {
      Random r = customRandomizerForThisThread();
      if (r != null)
        return r;
    }
    return ThreadLocalRandom.current();
  }

  static public Random getRandomizer(Random r) {
    return r != null ? r : defaultRandomGenerator();
  }

  static public <A> A oneOf(List<A> l) {
    if (empty(l))
      return null;
    int n = l.size();
    return n == 1 ? first(l) : l.get(defaultRandomizer().nextInt(n));
  }

  static public char oneOf(String s) {
    return empty(s) ? '?' : s.charAt(random(l(s)));
  }

  static public <A> A oneOf(A... l) {
    return oneOf(asList(l));
  }

  static public <A> A collectionGet(Collection<A> c, int idx) {
    if (c == null || idx < 0 || idx >= l(c))
      return null;
    if (c instanceof List)
      return listGet((List<A>) c, idx);
    Iterator<A> it = c.iterator();
    for (int i = 0; i < idx; i++) if (it.hasNext())
      it.next();
    else
      return null;
    return it.hasNext() ? it.next() : null;
  }

  static public <A, B> Pair<A, B> entryToPair(Map.Entry<A, B> e) {
    return mapEntryToPair(e);
  }

  static public <A, B> Set<Map.Entry<A, B>> entries(Map<A, B> map) {
    return _entrySet(map);
  }

  static public int boolToInt(boolean b) {
    return b ? 1 : 0;
  }

  static public class getOpt_Map extends WeakHashMap {

    public getOpt_Map() {
      if (getOpt_special == null)
        getOpt_special = new HashMap();
      clear();
    }

    public void clear() {
      super.clear();
      put(Class.class, getOpt_special);
      put(String.class, getOpt_special);
    }
  }

  static final public Map<Class, HashMap<String, Field>> getOpt_cache = _registerDangerousWeakMap(synchroMap(new getOpt_Map()));

  static public HashMap getOpt_special;

  static public Map<String, Field> getOpt_getFieldMap(Object o) {
    Class c = _getClass(o);
    HashMap<String, Field> map = getOpt_cache.get(c);
    if (map == null)
      map = getOpt_makeCache(c);
    return map;
  }

  static public Object getOpt_cached(Object o, String field) {
    try {
      if (o == null)
        return null;
      Map<String, Field> map = getOpt_getFieldMap(o);
      if (map == getOpt_special) {
        if (o instanceof Class)
          return getOpt((Class) o, field);
        if (o instanceof Map)
          return ((Map) o).get(field);
      }
      Field f = map.get(field);
      if (f != null)
        return f.get(o);
      if (o instanceof DynamicObject)
        return syncMapGet2(((DynamicObject) o).fieldValues, field);
      return null;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public HashMap<String, Field> getOpt_makeCache(Class c) {
    HashMap<String, Field> map;
    if (isSubtypeOf(c, Map.class))
      map = getOpt_special;
    else {
      map = new HashMap();
      if (!reflection_classesNotToScan().contains(c.getName())) {
        Class _c = c;
        do {
          for (Field f : _c.getDeclaredFields()) {
            makeAccessible(f);
            String name = f.getName();
            if (!map.containsKey(name))
              map.put(name, f);
          }
          _c = _c.getSuperclass();
        } while (_c != null);
      }
    }
    if (getOpt_cache != null)
      getOpt_cache.put(c, map);
    return map;
  }

  static public Field getOpt_findField(Class<?> c, String field) {
    Class _c = c;
    do {
      for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field))
        return f;
      _c = _c.getSuperclass();
    } while (_c != null);
    return null;
  }

  static public boolean rectContains(int x1, int y1, int w, int h, Pt p) {
    return p.x >= x1 && p.y >= y1 && p.x < x1 + w && p.y < y1 + h;
  }

  static public boolean rectContains(Rect a, Rect b) {
    return b.x >= a.x && b.y >= a.y && b.x2() <= a.x2() && b.y2() <= a.y2();
  }

  static public boolean rectContains(Rect a, Rectangle b) {
    return rectContains(a, toRect(b));
  }

  static public boolean rectContains(Rect a, int x, int y) {
    return a != null && a.contains(x, y);
  }

  static public boolean rectContains(Rect a, Pt p) {
    return a != null && p != null && a.contains(p);
  }

  static public String actualUserHome_value;

  static public String actualUserHome() {
    if (actualUserHome_value == null) {
      if (isAndroid())
        actualUserHome_value = "/storage/emulated/0/";
      else
        actualUserHome_value = System.getProperty("user.home");
    }
    return actualUserHome_value;
  }

  static public File actualUserHome(String sub) {
    return newFile(new File(actualUserHome()), sub);
  }

  static public <A, B> Map<A, B> newWeakHashMap() {
    return _registerWeakMap(synchroMap(new WeakHashMap()));
  }

  static public void newPing() {
    var tl = newPing_actionTL();
    Runnable action = tl == null ? null : tl.get();
    {
      if (action != null)
        action.run();
    }
  }

  static public boolean isTrue(Object o) {
    if (o instanceof Boolean)
      return ((Boolean) o).booleanValue();
    if (o == null)
      return false;
    if (o instanceof ThreadLocal)
      return isTrue(((ThreadLocal) o).get());
    throw fail(getClassName(o));
  }

  static public boolean isTrue(Boolean b) {
    return b != null && b.booleanValue();
  }

  static public void failIfUnlicensed() {
    assertTrue("license off", licensed());
  }

  static public Thread currentThread() {
    return Thread.currentThread();
  }

  static public String combinePrintParameters(String s, Object o) {
    return (endsWithLetterOrDigit(s) ? s + ": " : s) + o;
  }

  static public void ping_okInCleanUp() {
    if (ping_pauseAll || ping_anyActions)
      ping_impl(true);
  }

  static public Object getThreadLocal(Object o, String name) {
    ThreadLocal t = (ThreadLocal) (getOpt(o, name));
    return t != null ? t.get() : null;
  }

  static public <A> A getThreadLocal(ThreadLocal<A> tl) {
    return tl == null ? null : tl.get();
  }

  static public <A> A getThreadLocal(ThreadLocal<A> tl, A defaultValue) {
    return or(getThreadLocal(tl), defaultValue);
  }

  static public ThreadLocal<Object> print_byThread_dontCreate() {
    return print_byThread;
  }

  static public boolean isFalse(Object o) {
    return eq(false, o);
  }

  static public String getStackTrace(Throwable throwable) {
    lastException(throwable);
    return getStackTrace_noRecord(throwable);
  }

  static public String getStackTrace_noRecord(Throwable throwable) {
    StringWriter writer = new StringWriter();
    throwable.printStackTrace(new PrintWriter(writer));
    return hideCredentials(writer.toString());
  }

  static public String getStackTrace() {
    return getStackTrace_noRecord(new Throwable());
  }

  static public String getStackTrace(String msg) {
    return getStackTrace_noRecord(new Throwable(msg));
  }

  static public String fixNewLines(String s) {
    int i = indexOf(s, '\r');
    if (i < 0)
      return s;
    int l = s.length();
    StringBuilder out = new StringBuilder(l);
    out.append(s, 0, i);
    for (; i < l; i++) {
      char c = s.charAt(i);
      if (c != '\r')
        out.append(c);
      else {
        out.append('\n');
        if (i + 1 < l && s.charAt(i + 1) == '\n')
          ++i;
      }
    }
    return out.toString();
  }

  static public void print_append(Appendable buf, String s, int max) {
    try {
      synchronized (buf) {
        buf.append(s);
        if (buf instanceof StringBuffer)
          rotateStringBuffer(((StringBuffer) buf), max);
        else if (buf instanceof StringBuilder)
          rotateStringBuilder(((StringBuilder) buf), max);
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public void vmBus_send(String msg, Object... args) {
    Object arg = vmBus_wrapArgs(args);
    pcallFAll_minimalExceptionHandling(vm_busListeners_live(), msg, arg);
    pcallFAll_minimalExceptionHandling(vm_busListenersByMessage_live().get(msg), msg, arg);
  }

  static public void vmBus_send(String msg) {
    vmBus_send(msg, (Object) null);
  }

  static public Class mc() {
    return main.class;
  }

  static public ThreadLocal<Object> print_byThread() {
    synchronized (print_byThread_lock) {
      if (print_byThread == null)
        print_byThread = new ThreadLocal();
    }
    return print_byThread;
  }

  static public AutoCloseable tempInterceptPrint(F1<String, Boolean> f) {
    return tempSetThreadLocal(print_byThread(), f);
  }

  static public boolean methodIsStatic(Method m) {
    return (m.getModifiers() & Modifier.STATIC) != 0;
  }

  static public List<Pair> _registerDangerousWeakMap_preList;

  static public <A> A _registerDangerousWeakMap(A map) {
    return _registerDangerousWeakMap(map, null);
  }

  static public <A> A _registerDangerousWeakMap(A map, Object init) {
    callF(init, map);
    if (init instanceof String) {
      final String f = (String) init;
      init = new VF1<Map>() {

        public void get(Map map) {
          try {
            callMC(f, map);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "callMC(f, map)";
        }
      };
    }
    if (javax() == null) {
      if (_registerDangerousWeakMap_preList == null)
        _registerDangerousWeakMap_preList = synchroList();
      _registerDangerousWeakMap_preList.add(pair(map, init));
      return map;
    }
    call(javax(), "_registerDangerousWeakMap", map, init);
    return map;
  }

  static public void _onLoad_registerDangerousWeakMap() {
    assertNotNull(javax());
    if (_registerDangerousWeakMap_preList == null)
      return;
    for (Pair p : _registerDangerousWeakMap_preList) _registerDangerousWeakMap(p.a, p.b);
    _registerDangerousWeakMap_preList = null;
  }

  static public Map synchroMap() {
    return synchroHashMap();
  }

  static public <A, B> Map<A, B> synchroMap(Map<A, B> map) {
    return new SynchronizedMap(map);
  }

  static public <A> int iteratorCount_int_close(Iterator<A> i) {
    try {
      int n = 0;
      if (i != null)
        while (i.hasNext()) {
          i.next();
          ++n;
        }
      if (i instanceof AutoCloseable)
        ((AutoCloseable) i).close();
      return n;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Throwable getExceptionCause(Throwable e) {
    Throwable c = e.getCause();
    return c != null ? c : e;
  }

  static public String joinWithSpace(Iterable c) {
    return join(" ", c);
  }

  static public String joinWithSpace(Object... c) {
    return join(" ", c);
  }

  static public List<String> classNames(Collection l) {
    return getClassNames(l);
  }

  static public List<String> classNames(Object[] l) {
    return getClassNames(asList(l));
  }

  static public double toDouble(Object o) {
    if (o instanceof Number)
      return ((Number) o).doubleValue();
    if (o instanceof BigInteger)
      return ((BigInteger) o).doubleValue();
    if (o instanceof String)
      return parseDouble((String) o);
    if (o == null)
      return 0.0;
    throw fail(o);
  }

  static public <A> Iterator<A> iterator(Iterable<A> c) {
    return c == null ? emptyIterator() : c.iterator();
  }

  static public int isAndroid_flag;

  static public boolean isAndroid() {
    if (isAndroid_flag == 0)
      isAndroid_flag = System.getProperty("java.vendor").toLowerCase().indexOf("android") >= 0 ? 1 : -1;
    return isAndroid_flag > 0;
  }

  static public Boolean isHeadless_cache;

  static public boolean isHeadless() {
    if (isHeadless_cache != null)
      return isHeadless_cache;
    if (isAndroid())
      return isHeadless_cache = true;
    if (GraphicsEnvironment.isHeadless())
      return isHeadless_cache = true;
    try {
      SwingUtilities.isEventDispatchThread();
      return isHeadless_cache = false;
    } catch (Throwable e) {
      return isHeadless_cache = true;
    }
  }

  static public void assertFalse(Object o) {
    if (!(eq(o, false)))
      throw fail(str(o));
  }

  static public boolean assertFalse(boolean b) {
    if (b)
      throw fail("oops");
    return b;
  }

  static public boolean assertFalse(String msg, boolean b) {
    if (b)
      throw fail(msg);
    return b;
  }

  static public Object collectionMutex(List l) {
    return l;
  }

  static public Object collectionMutex(Object o) {
    if (o instanceof List)
      return o;
    String c = className(o);
    return o;
  }

  static public String[] dropFirst(int n, String[] a) {
    return drop(n, a);
  }

  static public String[] dropFirst(String[] a) {
    return drop(1, a);
  }

  static public Object[] dropFirst(Object[] a) {
    return drop(1, a);
  }

  static public <A> List<A> dropFirst(List<A> l) {
    return dropFirst(1, l);
  }

  static public <A> List<A> dropFirst(int n, Iterable<A> i) {
    return dropFirst(n, toList(i));
  }

  static public <A> List<A> dropFirst(Iterable<A> i) {
    return dropFirst(toList(i));
  }

  static public <A> List<A> dropFirst(int n, List<A> l) {
    return n <= 0 ? l : new ArrayList(l.subList(Math.min(n, l.size()), l.size()));
  }

  static public <A> List<A> dropFirst(List<A> l, int n) {
    return dropFirst(n, l);
  }

  static public String dropFirst(int n, String s) {
    return substring(s, n);
  }

  static public String dropFirst(String s, int n) {
    return substring(s, n);
  }

  static public String dropFirst(String s) {
    return substring(s, 1);
  }

  static public <A> Chain<A> dropFirst(Chain<A> c) {
    return c == null ? null : c.next;
  }

  static public int indexOfNonDigit(String s) {
    int n = l(s);
    for (int i = 0; i < n; i++) if (!isDigit(s.charAt(i)))
      return i;
    return -1;
  }

  static public String shortenClassName(String name) {
    if (name == null)
      return null;
    int i = lastIndexOf(name, "$");
    if (i < 0)
      i = lastIndexOf(name, ".");
    return i < 0 ? name : substring(name, i + 1);
  }

  static public Runnable addThreadInfoToRunnable(final Object r) {
    final Object info = _threadInfo();
    return info == null ? asRunnable(r) : new Runnable() {

      public void run() {
        try {
          _inheritThreadInfo(info);
          callF(r);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "_inheritThreadInfo(info); callF(r);";
      }
    };
  }

  static transient public Set<IVF1<Runnable>> onExecutingSwingCode;

  public static void onExecutingSwingCode(IVF1<Runnable> f) {
    onExecutingSwingCode = createOrAddToSyncLinkedHashSet(onExecutingSwingCode, f);
  }

  public static void removeExecutingSwingCodeListener(IVF1<Runnable> f) {
    main.remove(onExecutingSwingCode, f);
  }

  public static void executingSwingCode(Runnable code) {
    if (onExecutingSwingCode != null)
      for (var listener : onExecutingSwingCode) pcallF_typed(listener, code);
  }

  static public <A, B> Pair<A, B> mapEntryToPair(Map.Entry<A, B> e) {
    return e == null ? null : pair(e.getKey(), e.getValue());
  }

  static public <A, B> Set<Map.Entry<A, B>> entrySet(Map<A, B> map) {
    return _entrySet(map);
  }

  static public <A, B> Pair<A, B> pair(A a, B b) {
    return new Pair(a, b);
  }

  static public <A> Pair<A, A> pair(A a) {
    return new Pair(a, a);
  }

  static public <A> A firstThat(Iterable<A> l, IF1<A, Boolean> pred) {
    for (A a : unnullForIteration(l)) if (pred.get(a))
      return a;
    return null;
  }

  static public <A> A firstThat(A[] l, IF1<A, Boolean> pred) {
    for (A a : unnullForIteration(l)) if (pred.get(a))
      return a;
    return null;
  }

  static public <A> A firstThat(IF1<A, Boolean> pred, Iterable<A> l) {
    return firstThat(l, pred);
  }

  static public <A> A firstThat(IF1<A, Boolean> pred, A[] l) {
    return firstThat(l, pred);
  }

  static public WeakHasherMap<Symbol, Boolean> symbol_map = new WeakHasherMap(new Hasher<Symbol>() {

    public int hashCode(Symbol symbol) {
      return symbol.text.hashCode();
    }

    public boolean equals(Symbol a, Symbol b) {
      if (a == null)
        return b == null;
      return b != null && eq(a.text, b.text);
    }
  });

  static public Symbol symbol(String s) {
    if (s == null)
      return null;
    synchronized (symbol_map) {
      Symbol symbol = new Symbol(s, true);
      Symbol existingSymbol = symbol_map.findKey(symbol);
      if (existingSymbol == null)
        symbol_map.put(existingSymbol = symbol, true);
      return existingSymbol;
    }
  }

  static public Symbol symbol(CharSequence s) {
    if (s == null)
      return null;
    if (s instanceof Symbol)
      return (Symbol) s;
    if (s instanceof String)
      return symbol((String) s);
    return symbol(str(s));
  }

  static public Symbol symbol(Object o) {
    return symbol((CharSequence) o);
  }

  static public <A> A or(A a, A b) {
    return a != null ? a : b;
  }

  static public Random customRandomizerForThisThread() {
    return customRandomizerForThisThread_tl().get();
  }

  static public <A> A listGet(List<A> l, int idx) {
    return l != null && idx >= 0 && idx < l.size() ? l.get(idx) : null;
  }

  static public <A, B> Set<Map.Entry<A, B>> _entrySet(Map<A, B> map) {
    return map == null ? Collections.EMPTY_SET : map.entrySet();
  }

  static public void clear(Collection c) {
    if (c != null)
      c.clear();
  }

  static public void clear(Map map) {
    if (map != null)
      map.clear();
  }

  static public <A, B> void put(Map<A, B> map, A a, B b) {
    if (map != null)
      map.put(a, b);
  }

  static public <A> void put(List<A> l, int i, A a) {
    if (l != null && i >= 0 && i < l(l))
      l.set(i, a);
  }

  static public Class<?> _getClass(String name) {
    try {
      return Class.forName(name);
    } catch (ClassNotFoundException e) {
      return null;
    }
  }

  static public Class _getClass(Object o) {
    return o == null ? null : o instanceof Class ? (Class) o : o.getClass();
  }

  static public Class _getClass(Object realm, String name) {
    try {
      return classLoaderForObject(realm).loadClass(classNameToVM(name));
    } catch (ClassNotFoundException e) {
      return null;
    }
  }

  static public <A, B> B syncMapGet2(Map<A, B> map, A a) {
    if (map == null)
      return null;
    synchronized (collectionMutex(map)) {
      return map.get(a);
    }
  }

  static public <A, B> B syncMapGet2(A a, Map<A, B> map) {
    return syncMapGet2(map, a);
  }

  static public boolean isSubtypeOf(Class a, Class b) {
    return a != null && b != null && b.isAssignableFrom(a);
  }

  static public Set<String> reflection_classesNotToScan_value = litset("jdk.internal.loader.URLClassPath");

  static public Set<String> reflection_classesNotToScan() {
    return reflection_classesNotToScan_value;
  }

  static public Rect toRect(Rectangle r) {
    return r == null ? null : new Rect(r);
  }

  static public Rect toRect(RectangularShape r) {
    return r == null ? null : toRect(r.getBounds());
  }

  static public Rect toRect(Rect r) {
    return r;
  }

  static public List _registerWeakMap_preList;

  static public <A> A _registerWeakMap(A map) {
    if (javax() == null) {
      if (_registerWeakMap_preList == null)
        _registerWeakMap_preList = synchroList();
      _registerWeakMap_preList.add(map);
      return map;
    }
    try {
      call(javax(), "_registerWeakMap", map);
    } catch (Throwable e) {
      printException(e);
      print("Upgrade JavaX!!");
    }
    return map;
  }

  static public void _onLoad_registerWeakMap() {
    assertNotNull(javax());
    if (_registerWeakMap_preList == null)
      return;
    for (Object o : _registerWeakMap_preList) _registerWeakMap(o);
    _registerWeakMap_preList = null;
  }

  static public x30_pkg.x30_util.BetterThreadLocal<Runnable> newPing_actionTL;

  static public x30_pkg.x30_util.BetterThreadLocal<Runnable> newPing_actionTL() {
    if (newPing_actionTL == null)
      newPing_actionTL = vm_generalMap_getOrCreate("newPing_actionTL", () -> {
        Runnable value = (Runnable) (callF_gen(vm_generalMap_get("newPing_valueForNewThread")));
        var tl = new x30_pkg.x30_util.BetterThreadLocal<Runnable>();
        tl.set(value);
        return tl;
      });
    return newPing_actionTL;
  }

  static public void assertTrue(Object o) {
    if (!(eq(o, true)))
      throw fail(str(o));
  }

  static public boolean assertTrue(String msg, boolean b) {
    if (!b)
      throw fail(msg);
    return b;
  }

  static public boolean assertTrue(boolean b) {
    if (!b)
      throw fail("oops");
    return b;
  }

  static public boolean assertTrue(Scorer scorer, boolean b) {
    scorer.add(b);
    return b;
  }

  static volatile public boolean licensed_yes = true;

  static public boolean licensed() {
    if (!licensed_yes)
      return false;
    ping_okInCleanUp();
    return true;
  }

  static public void licensed_off() {
    licensed_yes = false;
  }

  static public boolean endsWithLetterOrDigit(String s) {
    return s != null && s.length() > 0 && Character.isLetterOrDigit(s.charAt(s.length() - 1));
  }

  static volatile public PersistableThrowable lastException_lastException;

  static public PersistableThrowable lastException() {
    return lastException_lastException;
  }

  static public void lastException(Throwable e) {
    lastException_lastException = persistableThrowable(e);
  }

  static public String hideCredentials(URL url) {
    return url == null ? null : hideCredentials(str(url));
  }

  static public String hideCredentials(String url) {
    try {
      if (startsWithOneOf(url, "http://", "https://") && isAGIBlueDomain(hostNameFromURL(url)))
        return url;
    } catch (Throwable e) {
      print("HideCredentials", e);
    }
    return url.replaceAll("([&?])(_pass|key|cookie)=[^&\\s\"]*", "$1$2=<hidden>");
  }

  static public String hideCredentials(Object o) {
    return hideCredentials(str(o));
  }

  static public <A> int indexOf(List<A> l, A a, int startIndex) {
    if (l == null)
      return -1;
    int n = l(l);
    for (int i = startIndex; i < n; i++) if (eq(l.get(i), a))
      return i;
    return -1;
  }

  static public <A> int indexOf(List<A> l, int startIndex, A a) {
    return indexOf(l, a, startIndex);
  }

  static public <A> int indexOf(List<A> l, A a) {
    if (l == null)
      return -1;
    return l.indexOf(a);
  }

  static public int indexOf(String a, String b) {
    return a == null || b == null ? -1 : a.indexOf(b);
  }

  static public int indexOf(String a, String b, int i) {
    return a == null || b == null ? -1 : a.indexOf(b, i);
  }

  static public int indexOf(String a, char b) {
    return a == null ? -1 : a.indexOf(b);
  }

  static public int indexOf(String a, int i, char b) {
    return indexOf(a, b, i);
  }

  static public int indexOf(String a, char b, int i) {
    return a == null ? -1 : a.indexOf(b, i);
  }

  static public int indexOf(String a, int i, String b) {
    return a == null || b == null ? -1 : a.indexOf(b, i);
  }

  static public <A> int indexOf(A[] x, A a) {
    int n = l(x);
    for (int i = 0; i < n; i++) if (eq(x[i], a))
      return i;
    return -1;
  }

  static public <A> int indexOf(Iterable<A> l, A a) {
    if (l == null)
      return -1;
    int i = 0;
    for (A x : l) {
      if (eq(x, a))
        return i;
      i++;
    }
    return -1;
  }

  static public void rotateStringBuffer(StringBuffer buf, int max) {
    try {
      if (buf == null)
        return;
      synchronized (buf) {
        if (buf.length() <= max)
          return;
        try {
          int newLength = max / 2;
          int ofs = buf.length() - newLength;
          String newString = buf.substring(ofs);
          buf.setLength(0);
          buf.append("[...] ").append(newString);
        } catch (Exception e) {
          buf.setLength(0);
        }
        buf.trimToSize();
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public void rotateStringBuilder(StringBuilder buf, int max) {
    try {
      if (buf == null)
        return;
      synchronized (buf) {
        if (buf.length() <= max)
          return;
        try {
          int newLength = max / 2;
          int ofs = buf.length() - newLength;
          String newString = buf.substring(ofs);
          buf.setLength(0);
          buf.append("[...] ").append(newString);
        } catch (Exception e) {
          buf.setLength(0);
        }
        buf.trimToSize();
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Object vmBus_wrapArgs(Object... args) {
    return empty(args) ? null : l(args) == 1 ? args[0] : args;
  }

  static public void pcallFAll_minimalExceptionHandling(Collection l, Object... args) {
    if (l != null)
      for (Object f : cloneList(l)) {
        ping();
        pcallF_minimalExceptionHandling(f, args);
      }
  }

  static public void pcallFAll_minimalExceptionHandling(Iterator it, Object... args) {
    while (it.hasNext()) {
      ping();
      pcallF_minimalExceptionHandling(it.next(), args);
    }
  }

  static public Set vm_busListeners_live_cache;

  static public Set vm_busListeners_live() {
    if (vm_busListeners_live_cache == null)
      vm_busListeners_live_cache = vm_busListeners_live_load();
    return vm_busListeners_live_cache;
  }

  static public Set vm_busListeners_live_load() {
    return vm_generalIdentityHashSet("busListeners");
  }

  static public Map<String, Set> vm_busListenersByMessage_live_cache;

  static public Map<String, Set> vm_busListenersByMessage_live() {
    if (vm_busListenersByMessage_live_cache == null)
      vm_busListenersByMessage_live_cache = vm_busListenersByMessage_live_load();
    return vm_busListenersByMessage_live_cache;
  }

  static public Map<String, Set> vm_busListenersByMessage_live_load() {
    return vm_generalHashMap("busListenersByMessage");
  }

  static public <A> AutoCloseable tempSetThreadLocal(final ThreadLocal<A> tl, A a) {
    if (tl == null)
      return null;
    final A prev = setThreadLocal(tl, a);
    return new AutoCloseable() {

      public String toString() {
        return "tl.set(prev);";
      }

      public void close() throws Exception {
        tl.set(prev);
      }
    };
  }

  static public HashMap<String, List<Method>> callMC_cache = new HashMap();

  static public String callMC_key;

  static public Method callMC_value;

  static public Object callMC(String method, String[] arg) {
    return callMC(method, new Object[] { arg });
  }

  static public Object callMC(String method, Object... args) {
    try {
      Method me;
      if (callMC_cache == null)
        callMC_cache = new HashMap();
      synchronized (callMC_cache) {
        me = method == callMC_key ? callMC_value : null;
      }
      if (me != null)
        try {
          return invokeMethod(me, null, args);
        } catch (IllegalArgumentException e) {
          throw new RuntimeException("Can't call " + me + " with arguments " + classNames(args), e);
        }
      List<Method> m;
      synchronized (callMC_cache) {
        m = callMC_cache.get(method);
      }
      if (m == null) {
        if (callMC_cache.isEmpty()) {
          callMC_makeCache();
          m = callMC_cache.get(method);
        }
        if (m == null)
          throw fail("Method named " + method + " not found in main");
      }
      int n = m.size();
      if (n == 1) {
        me = m.get(0);
        synchronized (callMC_cache) {
          callMC_key = method;
          callMC_value = me;
        }
        try {
          return invokeMethod(me, null, args);
        } catch (IllegalArgumentException e) {
          throw new RuntimeException("Can't call " + me + " with arguments " + classNames(args), e);
        }
      }
      for (int i = 0; i < n; i++) {
        me = m.get(i);
        if (call_checkArgs(me, args, false))
          return invokeMethod(me, null, args);
      }
      throw fail("No method called " + method + " with arguments (" + joinWithComma(getClasses(args)) + ") found in main");
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public void callMC_makeCache() {
    synchronized (callMC_cache) {
      callMC_cache.clear();
      Class _c = (Class) mc(), c = _c;
      while (c != null) {
        for (Method m : c.getDeclaredMethods()) if ((m.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0) {
          makeAccessible(m);
          multiMapPut(callMC_cache, m.getName(), m);
        }
        c = c.getSuperclass();
      }
    }
  }

  static public Class javax() {
    return getJavaX();
  }

  static public <A> List<A> synchroList() {
    return synchroList(new ArrayList<A>());
  }

  static public <A> List<A> synchroList(List<A> l) {
    return new SynchronizedList(l);
  }

  static public Object call(Object o) {
    return callF(o);
  }

  static public Object call(Object o, String method, String[] arg) {
    return call(o, method, new Object[] { arg });
  }

  static public Object call(Object o, String method, Object... args) {
    return call_withVarargs(o, method, args);
  }

  static public <A> A assertNotNull(A a) {
    assertTrue(a != null);
    return a;
  }

  static public <A> A assertNotNull(String msg, A a) {
    assertTrue(msg, a != null);
    return a;
  }

  static public <A> A assertNotNull(Scorer scorer, String msg, A a) {
    if (scorer == null)
      return assertNotNull(msg, a);
    if (a == null) {
      print("BAD - " + msg + " is null: " + a);
      scorer.add(false);
    } else {
      print("OK, " + msg + " not null: " + a);
      scorer.add(true);
    }
    return a;
  }

  static public Map synchroHashMap() {
    return synchronizedMap(new HashMap());
  }

  static public List<String> getClassNames(Collection l) {
    List<String> out = new ArrayList();
    if (l != null)
      for (Object o : l) out.add(o == null ? null : getClassName(o));
    return out;
  }

  static public double parseDouble(String s) {
    return empty(s) ? 0.0 : Double.parseDouble(s);
  }

  static public Iterator emptyIterator() {
    return Collections.emptyIterator();
  }

  static public String className(Object o) {
    return getClassName(o);
  }

  static public String[] drop(int n, String[] a) {
    n = Math.min(n, a.length);
    String[] b = new String[a.length - n];
    System.arraycopy(a, n, b, 0, b.length);
    return b;
  }

  static public Object[] drop(int n, Object[] a) {
    n = Math.min(n, a.length);
    Object[] b = new Object[a.length - n];
    System.arraycopy(a, n, b, 0, b.length);
    return b;
  }

  static public <A> List<A> drop(Iterable<A> c, Object pred) {
    return antiFilter(c, pred);
  }

  static public List drop(Object pred, Iterable c) {
    return antiFilter(pred, c);
  }

  static public List drop(Object pred, Object[] c) {
    return antiFilter(pred, c);
  }

  static public <A, B extends A> List<B> drop(Iterable<B> c, F1<A, Boolean> pred) {
    return antiFilter(c, pred);
  }

  static public <A, B extends A> List<B> drop(F1<A, Boolean> pred, Iterable<B> c) {
    return antiFilter(pred, c);
  }

  static public <A, B extends A> List<B> drop(Iterable<B> c, IF1<A, Boolean> pred) {
    return antiFilter(c, pred);
  }

  static public <A, B extends A> List<B> drop(IF1<A, Boolean> pred, Iterable<B> c) {
    return antiFilter(pred, c);
  }

  static public <A> ArrayList<A> toList(A[] a) {
    return asList(a);
  }

  static public ArrayList<Integer> toList(int[] a) {
    return asList(a);
  }

  static public ArrayList<Short> toList(short[] a) {
    return asList(a);
  }

  static public ArrayList<Long> toList(long[] a) {
    return asList(a);
  }

  static public ArrayList<Double> toList(double[] a) {
    return asList(a);
  }

  static public ArrayList<Float> toList(float[] a) {
    return asList(a);
  }

  static public <A> ArrayList<A> toList(Set<A> s) {
    return asList(s);
  }

  static public <A> ArrayList<A> toList(Iterable<A> s) {
    return asList(s);
  }

  static public String substring(String s, int x) {
    return substring(s, x, strL(s));
  }

  static public String substring(String s, int x, int y) {
    if (s == null)
      return null;
    if (x < 0)
      x = 0;
    int n = s.length();
    if (y < x)
      y = x;
    if (y > n)
      y = n;
    if (x >= y)
      return "";
    return s.substring(x, y);
  }

  static public String substring(String s, IntRange r) {
    return r == null ? null : substring(s, r.start, r.end);
  }

  static public String substring(String s, CharSequence l) {
    return substring(s, lCharSequence(l));
  }

  static public boolean isDigit(char c) {
    return Character.isDigit(c);
  }

  static public int lastIndexOf(String a, String b) {
    return a == null || b == null ? -1 : a.lastIndexOf(b);
  }

  static public int lastIndexOf(String a, char b) {
    return a == null ? -1 : a.lastIndexOf(b);
  }

  static public <A> int lastIndexOf(List<A> l, int i, A a) {
    if (l == null)
      return -1;
    for (i = min(l(l), i) - 1; i >= 0; i--) if (eq(l.get(i), a))
      return i;
    return -1;
  }

  static public <A> int lastIndexOf(List<A> l, A a) {
    if (l == null)
      return -1;
    for (int i = l(l) - 1; i >= 0; i--) if (eq(l.get(i), a))
      return i;
    return -1;
  }

  static public List<VF1<Map>> _threadInfo_makers = synchroList();

  static public Object _threadInfo() {
    if (empty(_threadInfo_makers))
      return null;
    HashMap map = new HashMap();
    pcallFAll(_threadInfo_makers, map);
    return map;
  }

  static public Runnable asRunnable(Object o) {
    return toRunnable(o);
  }

  static public void _inheritThreadInfo(Object info) {
    _threadInheritInfo(info);
  }

  static public <A> Set<A> createOrAddToSyncLinkedHashSet(Set<A> set, A a) {
    if (set == null)
      set = syncLinkedHashSet();
    set.add(a);
    return set;
  }

  static public Class main() {
    return getMainClass();
  }

  static public <A> void remove(List<A> l, int i) {
    if (l != null && i >= 0 && i < l(l))
      l.remove(i);
  }

  static public <A> void remove(Collection<A> l, A a) {
    if (l != null)
      l.remove(a);
  }

  static public <A, B> B remove(Map<A, B> map, Object a) {
    return map == null ? null : map.remove(a);
  }

  static public void remove(BitSet bs, int i) {
    bs.clear(i);
  }

  static public <A> A pcallF_typed(F0<A> f) {
    try {
      return f == null ? null : f.get();
    } catch (Throwable __e) {
      pcallFail(__e);
    }
    return null;
  }

  static public <A, B> B pcallF_typed(F1<A, B> f, A a) {
    try {
      return f == null ? null : f.get(a);
    } catch (Throwable __e) {
      pcallFail(__e);
    }
    return null;
  }

  static public <A> void pcallF_typed(VF1<A> f, A a) {
    try {
      {
        if (f != null)
          f.get(a);
      }
    } catch (Throwable __e) {
      pcallFail(__e);
    }
  }

  static public <A> void pcallF_typed(IVF1<A> f, A a) {
    try {
      {
        if (f != null)
          f.get(a);
      }
    } catch (Throwable __e) {
      pcallFail(__e);
    }
  }

  static public <A, B> void pcallF_typed(IVF2<A, B> f, A a, B b) {
    try {
      {
        if (f != null)
          f.get(a, b);
      }
    } catch (Throwable __e) {
      pcallFail(__e);
    }
  }

  static public Object pcallF_typed(Runnable r) {
    try {
      {
        if (r != null)
          r.run();
      }
    } catch (Throwable __e) {
      pcallFail(__e);
    }
    return null;
  }

  static public <A> A pcallF_typed(IF0<A> f) {
    try {
      return f == null ? null : f.get();
    } catch (Throwable __e) {
      pcallFail(__e);
    }
    return null;
  }

  static public <A, B> B pcallF_typed(IF1<A, B> f, A a) {
    try {
      return f == null ? null : f.get(a);
    } catch (Throwable __e) {
      pcallFail(__e);
    }
    return null;
  }

  static public String unnullForIteration(String s) {
    return s == null ? "" : s;
  }

  static public <A> Collection<A> unnullForIteration(Collection<A> l) {
    return l == null ? immutableEmptyList() : l;
  }

  static public <A> List<A> unnullForIteration(List<A> l) {
    return l == null ? immutableEmptyList() : l;
  }

  static public byte[] unnullForIteration(byte[] l) {
    return l == null ? emptyByteArray() : l;
  }

  static public int[] unnullForIteration(int[] l) {
    return l == null ? emptyIntArray() : l;
  }

  static public char[] unnullForIteration(char[] l) {
    return l == null ? emptyCharArray() : l;
  }

  static public double[] unnullForIteration(double[] l) {
    return l == null ? emptyDoubleArray() : l;
  }

  static public short[] unnullForIteration(short[] l) {
    return l == null ? emptyShortArray() : l;
  }

  static public <A, B> Map<A, B> unnullForIteration(Map<A, B> l) {
    return l == null ? immutableEmptyMap() : l;
  }

  static public <A> Iterable<A> unnullForIteration(Iterable<A> i) {
    return i == null ? immutableEmptyList() : i;
  }

  static public <A> A[] unnullForIteration(A[] a) {
    return a == null ? (A[]) emptyObjectArray() : a;
  }

  static public BitSet unnullForIteration(BitSet b) {
    return b == null ? new BitSet() : b;
  }

  static public Pt unnullForIteration(Pt p) {
    return p == null ? new Pt() : p;
  }

  static public Symbol unnullForIteration(Symbol s) {
    return s == null ? emptySymbol() : s;
  }

  static public <A, B> Pair<A, B> unnullForIteration(Pair<A, B> p) {
    return p != null ? p : new Pair(null, null);
  }

  static public long unnullForIteration(Long l) {
    return l == null ? 0L : l;
  }

  static public ThreadLocal<Random> customRandomizerForThisThread_tl = new ThreadLocal();

  static public ThreadLocal<Random> customRandomizerForThisThread_tl() {
    return customRandomizerForThisThread_tl;
  }

  static public ClassLoader classLoaderForObject(Object o) {
    if (o instanceof ClassLoader)
      return ((ClassLoader) o);
    if (o == null)
      return null;
    return _getClass(o).getClassLoader();
  }

  static public String classNameToVM(String name) {
    return name.replace(".", "$");
  }

  static public <A> HashSet<A> litset(A... items) {
    return lithashset(items);
  }

  static public int iround(double d) {
    return (int) Math.round(d);
  }

  static public int iround(Number n) {
    return iround(toDouble(n));
  }

  static public <A extends Throwable> A printException(A e) {
    printStackTrace(e);
    return e;
  }

  static public <A> A vm_generalMap_getOrCreate(Object key, F0<A> create) {
    return vm_generalMap_getOrCreate(key, f0ToIF0(create));
  }

  static public <A> A vm_generalMap_getOrCreate(Object key, IF0<A> create) {
    Map generalMap = vm_generalMap();
    if (generalMap == null)
      return null;
    synchronized (generalMap) {
      A a = (A) (vm_generalMap_get(key));
      if (a == null)
        vm_generalMap_put(key, a = create == null ? null : create.get());
      return a;
    }
  }

  static public <A> A callF_gen(F0<A> f) {
    return f == null ? null : f.get();
  }

  static public <A, B> B callF_gen(F1<A, B> f, A a) {
    return f == null ? null : f.get(a);
  }

  static public <A> A callF_gen(IF0<A> f) {
    return f == null ? null : f.get();
  }

  static public <A, B> B callF_gen(IF1<A, B> f, A a) {
    return f == null ? null : f.get(a);
  }

  static public <A, B> B callF_gen(A a, IF1<A, B> f) {
    return f == null ? null : f.get(a);
  }

  static public <A, B, C> C callF_gen(IF2<A, B, C> f, A a, B b) {
    return f == null ? null : f.get(a, b);
  }

  static public <A> void callF_gen(VF1<A> f, A a) {
    {
      if (f != null)
        f.get(a);
    }
  }

  static public <A> void callF_gen(A a, IVF1<A> f) {
    {
      if (f != null)
        f.get(a);
    }
  }

  static public <A> void callF_gen(IVF1<A> f, A a) {
    {
      if (f != null)
        f.get(a);
    }
  }

  static public Object callF_gen(Runnable r) {
    {
      if (r != null)
        r.run();
    }
    return null;
  }

  static public Object callF_gen(Object f, Object... args) {
    return callF(f, args);
  }

  static public Object vm_generalMap_get(Object key) {
    return vm_generalMap().get(key);
  }

  static public PersistableThrowable persistableThrowable(Throwable e) {
    return e == null ? null : new PersistableThrowable(e);
  }

  static public boolean startsWithOneOf(String s, String... l) {
    for (String x : l) if (startsWith(s, x))
      return true;
    return false;
  }

  static public boolean startsWithOneOf(String s, Matches m, String... l) {
    for (String x : l) if (startsWith(s, x, m))
      return true;
    return false;
  }

  static public boolean isAGIBlueDomain(String domain) {
    return domainIsUnder(domain, theAGIBlueDomain());
  }

  static public String hostNameFromURL(String url) {
    try {
      return empty(url) ? null : new URL(url).getHost();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Object pcallF_minimalExceptionHandling(Object f, Object... args) {
    try {
      return callFunction(f, args);
    } catch (Throwable e) {
      System.out.println(getStackTrace(e));
      _storeException(e);
    }
    return null;
  }

  static public Set vm_generalIdentityHashSet(Object name) {
    synchronized (vm_generalMap()) {
      Set set = (Set) (vm_generalMap_get(name));
      if (set == null)
        vm_generalMap_put(name, set = syncIdentityHashSet());
      return set;
    }
  }

  static public Map vm_generalHashMap(Object name) {
    synchronized (vm_generalMap()) {
      Map m = (Map) (vm_generalMap_get(name));
      if (m == null)
        vm_generalMap_put(name, m = syncHashMap());
      return m;
    }
  }

  static public <A> A setThreadLocal(ThreadLocal<A> tl, A value) {
    if (tl == null)
      return null;
    A old = tl.get();
    tl.set(value);
    return old;
  }

  static public <A> String joinWithComma(Iterable<A> c) {
    return join(", ", c);
  }

  static public String joinWithComma(Object... c) {
    return join(", ", c);
  }

  static public String joinWithComma(String... c) {
    return join(", ", c);
  }

  static public String joinWithComma(Pair p) {
    return p == null ? "" : joinWithComma(str(p.a), str(p.b));
  }

  static public List<Class> getClasses(Object[] array) {
    List<Class> l = emptyList(l(array));
    for (Object o : array) l.add(_getClass(o));
    return l;
  }

  static public <A, B> void multiMapPut(Map<A, List<B>> map, A a, B b) {
    List<B> l = map.get(a);
    if (l == null)
      map.put(a, l = new ArrayList());
    l.add(b);
  }

  static public <A, B> void multiMapPut(MultiMap<A, B> mm, A key, B value) {
    if (mm != null && key != null && value != null)
      mm.put(key, value);
  }

  static public Class __javax;

  static public Class getJavaX() {
    try {
      return __javax;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public void __setJavaX(Class j) {
    __javax = j;
    _onJavaXSet();
  }

  static public Object call_withVarargs(Object o, String methodName, Object... args) {
    try {
      if (o == null)
        return null;
      if (o instanceof Class) {
        Class c = (Class) o;
        _MethodCache cache = callOpt_getCache(c);
        Method me = cache.findStaticMethod(methodName, args);
        if (me != null)
          return invokeMethod(me, null, args);
        List<Method> methods = cache.cache.get(methodName);
        if (methods != null)
          methodSearch: for (Method m : methods) {
            {
              if (!(m.isVarArgs()))
                continue;
            }
            {
              if (!(isStaticMethod(m)))
                continue;
            }
            Object[] newArgs = massageArgsForVarArgsCall(m, args);
            if (newArgs != null)
              return invokeMethod(m, null, newArgs);
          }
        throw fail("Method " + c.getName() + "." + methodName + "(" + formatArgumentClasses(args) + ") not found");
      } else {
        Class c = o.getClass();
        _MethodCache cache = callOpt_getCache(c);
        Method me = cache.findMethod(methodName, args);
        if (me != null)
          return invokeMethod(me, o, args);
        List<Method> methods = cache.cache.get(methodName);
        if (methods != null)
          methodSearch: for (Method m : methods) {
            {
              if (!(m.isVarArgs()))
                continue;
            }
            Object[] newArgs = massageArgsForVarArgsCall(m, args);
            if (newArgs != null)
              return invokeMethod(m, o, newArgs);
          }
        throw fail("Method " + c.getName() + "." + methodName + "(" + formatArgumentClasses(args) + ") not found");
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Map synchronizedMap() {
    return synchroMap();
  }

  static public <A, B> Map<A, B> synchronizedMap(Map<A, B> map) {
    return synchroMap(map);
  }

  static public <A> List<A> antiFilter(Iterable<A> c, Object pred) {
    if (pred instanceof F1)
      return antiFilter(c, (F1<A, Boolean>) pred);
    List x = new ArrayList();
    if (c != null)
      for (Object o : c) if (!isTrue(callF(pred, o)))
        x.add(o);
    return x;
  }

  static public List antiFilter(Object pred, Iterable c) {
    return antiFilter(c, pred);
  }

  static public List antiFilter(Object pred, Object[] c) {
    return antiFilter(pred, wrapArrayAsList(c));
  }

  static public <A, B extends A> List<B> antiFilter(Iterable<B> c, F1<A, Boolean> pred) {
    List x = new ArrayList();
    if (c != null)
      for (B o : c) if (!pred.get(o).booleanValue())
        x.add(o);
    return x;
  }

  static public <A, B extends A> List<B> antiFilter(F1<A, Boolean> pred, Iterable<B> c) {
    return antiFilter(c, pred);
  }

  static public <A, B extends A> List<B> antiFilter(Iterable<B> c, IF1<A, Boolean> pred) {
    List x = new ArrayList();
    if (c != null)
      for (B o : c) if (!pred.get(o).booleanValue())
        x.add(o);
    return x;
  }

  static public <A, B extends A> List<B> antiFilter(IF1<A, Boolean> pred, Iterable<B> c) {
    return antiFilter(c, pred);
  }

  static public int strL(String s) {
    return s == null ? 0 : s.length();
  }

  static public int lCharSequence(CharSequence s) {
    return s == null ? 0 : s.length();
  }

  static public int min(int a, int b) {
    return Math.min(a, b);
  }

  static public long min(long a, long b) {
    return Math.min(a, b);
  }

  static public float min(float a, float b) {
    return Math.min(a, b);
  }

  static public float min(float a, float b, float c) {
    return min(min(a, b), c);
  }

  static public double min(double a, double b) {
    return Math.min(a, b);
  }

  static public double min(double[] c) {
    double x = Double.MAX_VALUE;
    for (double d : c) x = Math.min(x, d);
    return x;
  }

  static public float min(float[] c) {
    float x = Float.MAX_VALUE;
    for (float d : c) x = Math.min(x, d);
    return x;
  }

  static public byte min(byte[] c) {
    byte x = 127;
    for (byte d : c) if (d < x)
      x = d;
    return x;
  }

  static public short min(short[] c) {
    short x = 0x7FFF;
    for (short d : c) if (d < x)
      x = d;
    return x;
  }

  static public int min(int[] c) {
    int x = Integer.MAX_VALUE;
    for (int d : c) if (d < x)
      x = d;
    return x;
  }

  static public <A extends Comparable<A>> A min(A a, A b) {
    return cmp(a, b) <= 0 ? a : b;
  }

  static public void pcallFAll(Collection l, Object... args) {
    if (l != null)
      for (Object f : cloneList(l)) pcallF(f, args);
  }

  static public void pcallFAll(Iterator it, Object... args) {
    while (it.hasNext()) pcallF(it.next(), args);
  }

  static public Runnable toRunnable(final Object o) {
    if (o == null)
      return null;
    if (o instanceof Runnable)
      return (Runnable) o;
    if (o instanceof String)
      throw fail("callF_legacy");
    return new Runnable() {

      public void run() {
        try {
          callF(o);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "callF(o)";
      }
    };
  }

  static public List<VF1<Map>> _threadInheritInfo_retrievers = synchroList();

  static public void _threadInheritInfo(Object info) {
    if (info == null)
      return;
    pcallFAll(_threadInheritInfo_retrievers, (Map) info);
  }

  static public <A> Set<A> syncLinkedHashSet() {
    return synchroLinkedHashSet();
  }

  static public Class getMainClass() {
    return mc();
  }

  static public Class getMainClass(Object o) {
    try {
      if (o == null)
        return null;
      if (o instanceof Class && eq(((Class) o).getName(), "x30"))
        return (Class) o;
      ClassLoader cl = (o instanceof Class ? (Class) o : o.getClass()).getClassLoader();
      if (cl == null)
        return null;
      String name = mainClassNameForClassLoader(cl);
      return loadClassFromClassLoader_orNull(cl, name);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public void pcallFail(Throwable e) {
    pcallPolicyForThread().handlePcallFail(e);
  }

  static public void pcallFail(String msg) {
    pcallFail(new Throwable(msg));
  }

  static public <A> List<A> immutableEmptyList() {
    return Collections.emptyList();
  }

  static public byte[] emptyByteArray_a = new byte[0];

  static public byte[] emptyByteArray() {
    return emptyByteArray_a;
  }

  static public short[] emptyShortArray = new short[0];

  static public short[] emptyShortArray() {
    return emptyShortArray;
  }

  static public <A, B> Map<A, B> immutableEmptyMap() {
    return Collections.emptyMap();
  }

  static public <A> HashSet<A> lithashset(A... items) {
    HashSet<A> set = new HashSet();
    for (A a : items) set.add(a);
    return set;
  }

  static public <A extends Throwable> A printStackTrace(A e) {
    if (e != null)
      print(getStackTrace(e));
    return e;
  }

  static public void printStackTrace() {
    printStackTrace(new Throwable());
  }

  static public void printStackTrace(String msg) {
    printStackTrace(new Throwable(msg));
  }

  static public void printStackTrace(String msg, Throwable e) {
    printStackTrace(new Throwable(msg, e));
  }

  static public <A> IF0<A> f0ToIF0(F0<A> f) {
    return f == null ? null : () -> f.get();
  }

  static public Map vm_generalMap_map;

  static public Map vm_generalMap() {
    if (vm_generalMap_map == null)
      vm_generalMap_map = (Map) get(javax(), "generalMap");
    return vm_generalMap_map;
  }

  static public Object vm_generalMap_put(Object key, Object value) {
    return mapPutOrRemove(vm_generalMap(), key, value);
  }

  static public boolean startsWith(String a, String b) {
    return a != null && a.startsWith(unnull(b));
  }

  static public boolean startsWith(String a, char c) {
    return nemptyString(a) && a.charAt(0) == c;
  }

  static public boolean startsWith(String a, String b, Matches m) {
    if (!startsWith(a, b))
      return false;
    if (m != null)
      m.m = new String[] { substring(a, strL(b)) };
    return true;
  }

  static public boolean startsWith(List a, List b) {
    if (a == null || listL(b) > listL(a))
      return false;
    for (int i = 0; i < listL(b); i++) if (neq(a.get(i), b.get(i)))
      return false;
    return true;
  }

  static public boolean domainIsUnder(String domain, String mainDomain) {
    return eqic(domain, mainDomain) || ewic(domain, "." + mainDomain);
  }

  static public String theAGIBlueDomain() {
    return "agi.blue";
  }

  static public Object callFunction(Object f, Object... args) {
    return callF(f, args);
  }

  static public Throwable _storeException_value;

  static public void _storeException(Throwable e) {
    _storeException_value = e;
  }

  static public <A> Set<A> syncIdentityHashSet() {
    return (Set) synchronizedSet(identityHashSet());
  }

  static public Map syncHashMap() {
    return synchroHashMap();
  }

  static public void _onJavaXSet() {
  }

  static public Object[] massageArgsForVarArgsCall(Executable m, Object[] args) {
    Class<?>[] types = m.getParameterTypes();
    int n = types.length - 1, nArgs = l(args);
    if (nArgs < n)
      return null;
    for (int i = 0; i < n; i++) if (!argumentCompatibleWithType(args[i], types[i]))
      return null;
    Class varArgType = types[n].getComponentType();
    for (int i = n; i < nArgs; i++) if (!argumentCompatibleWithType(args[i], varArgType))
      return null;
    Object[] newArgs = new Object[n + 1];
    arraycopy(args, 0, newArgs, 0, n);
    int nVarArgs = nArgs - n;
    Object varArgs = Array.newInstance(varArgType, nVarArgs);
    for (int i = 0; i < nVarArgs; i++) Array.set(varArgs, i, args[n + i]);
    newArgs[n] = varArgs;
    return newArgs;
  }

  static public String formatArgumentClasses(Object[] args) {
    return joinWithComma(map(__18 -> getClassName(__18), args));
  }

  static public <A> List<A> wrapArrayAsList(A[] a) {
    return a == null ? null : Arrays.asList(a);
  }

  static public Object pcallF(Object f, Object... args) {
    return pcallFunction(f, args);
  }

  static public <A> A pcallF(F0<A> f) {
    try {
      return f == null ? null : f.get();
    } catch (Throwable __e) {
      pcallFail(__e);
    }
    return null;
  }

  static public <A, B> B pcallF(F1<A, B> f, A a) {
    try {
      return f == null ? null : f.get(a);
    } catch (Throwable __e) {
      pcallFail(__e);
    }
    return null;
  }

  static public <A> void pcallF(VF1<A> f, A a) {
    try {
      {
        if (f != null)
          f.get(a);
      }
    } catch (Throwable __e) {
      pcallFail(__e);
    }
  }

  static public Object pcallF(Runnable r) {
    try {
      {
        if (r != null)
          r.run();
      }
    } catch (Throwable __e) {
      pcallFail(__e);
    }
    return null;
  }

  static public <A> A pcallF(IF0<A> f) {
    try {
      return f == null ? null : f.get();
    } catch (Throwable __e) {
      pcallFail(__e);
    }
    return null;
  }

  static public <A, B> B pcallF(IF1<A, B> f, A a) {
    try {
      return f == null ? null : f.get(a);
    } catch (Throwable __e) {
      pcallFail(__e);
    }
    return null;
  }

  static public <A> Set<A> synchroLinkedHashSet() {
    return synchronizedSet(new CompactLinkedHashSet());
  }

  static public String mainClassNameForClassLoader(ClassLoader cl) {
    return or((String) callOpt(cl, "mainClassName"), "main");
  }

  static public Class loadClassFromClassLoader_orNull(ClassLoader cl, String name) {
    try {
      return cl == null ? null : cl.loadClass(name);
    } catch (ClassNotFoundException e) {
      return null;
    }
  }

  static public PCallPolicy pcallPolicyForThread() {
    var policy = pcallPolicyForThread_tl().get();
    if (policy != null)
      return policy;
    return defaultPCallPolicy();
  }

  static public <A> A get(List<A> l, int idx) {
    return l != null && idx >= 0 && idx < l(l) ? l.get(idx) : null;
  }

  static public <A> A get(A[] l, int idx) {
    return idx >= 0 && idx < l(l) ? l[idx] : null;
  }

  static public boolean get(boolean[] l, int idx) {
    return idx >= 0 && idx < l(l) ? l[idx] : false;
  }

  static public Object get(Object o, String field) {
    try {
      if (o == null)
        return null;
      if (o instanceof Class)
        return get((Class) o, field);
      if (o instanceof Map)
        return ((Map) o).get(field);
      Field f = getOpt_findField(o.getClass(), field);
      if (f != null) {
        makeAccessible(f);
        return f.get(o);
      }
      if (o instanceof DynamicObject)
        return getOptDynOnly(((DynamicObject) o), field);
    } catch (Exception e) {
      throw asRuntimeException(e);
    }
    throw new RuntimeException("Field '" + field + "' not found in " + o.getClass().getName());
  }

  static public Object get_raw(String field, Object o) {
    return get_raw(o, field);
  }

  static public Object get_raw(Object o, String field) {
    try {
      if (o == null)
        return null;
      Field f = get_findField(o.getClass(), field);
      makeAccessible(f);
      return f.get(o);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Object get(Class c, String field) {
    try {
      Field f = get_findStaticField(c, field);
      makeAccessible(f);
      return f.get(null);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  static public Field get_findStaticField(Class<?> c, String field) {
    Class _c = c;
    do {
      for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0)
        return f;
      _c = _c.getSuperclass();
    } while (_c != null);
    throw new RuntimeException("Static field '" + field + "' not found in " + c.getName());
  }

  static public Field get_findField(Class<?> c, String field) {
    Class _c = c;
    do {
      for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field))
        return f;
      _c = _c.getSuperclass();
    } while (_c != null);
    throw new RuntimeException("Field '" + field + "' not found in " + c.getName());
  }

  static public Object get(String field, Object o) {
    return get(o, field);
  }

  static public boolean get(BitSet bs, int idx) {
    return bs != null && bs.get(idx);
  }

  static public <A, B> B mapPutOrRemove(Map<A, B> map, A key, B value) {
    if (map != null && key != null)
      if (value != null)
        return map.put(key, value);
      else
        return map.remove(key);
    return null;
  }

  static public boolean nemptyString(String s) {
    return s != null && s.length() > 0;
  }

  static public int listL(Collection l) {
    return l == null ? 0 : l.size();
  }

  static public boolean neq(Object a, Object b) {
    return !eq(a, b);
  }

  static public boolean eqic(String a, String b) {
    if ((a == null) != (b == null))
      return false;
    if (a == null)
      return true;
    return a.equalsIgnoreCase(b);
  }

  static public boolean eqic(Symbol a, Symbol b) {
    return eq(a, b);
  }

  static public boolean eqic(Symbol a, String b) {
    return eqic(asString(a), b);
  }

  static public boolean eqic(char a, char b) {
    if (a == b)
      return true;
    char u1 = Character.toUpperCase(a);
    char u2 = Character.toUpperCase(b);
    if (u1 == u2)
      return true;
    return Character.toLowerCase(u1) == Character.toLowerCase(u2);
  }

  static public boolean ewic(String a, String b) {
    return endsWithIgnoreCase(a, b);
  }

  static public boolean ewic(String a, String b, Matches m) {
    return endsWithIgnoreCase(a, b, m);
  }

  static public <A> Set<A> synchronizedSet() {
    return synchroHashSet();
  }

  static public <A> Set<A> synchronizedSet(Set<A> set) {
    return new SynchronizedSet(set);
  }

  static public <A> Set<A> identityHashSet() {
    return Collections.newSetFromMap(new IdentityHashMap());
  }

  static public boolean argumentCompatibleWithType(Object arg, Class type) {
    return arg == null ? !type.isPrimitive() : isInstanceX(type, arg);
  }

  static public void arraycopy(Object[] a, Object[] b) {
    if (a != null && b != null)
      arraycopy(a, 0, b, 0, Math.min(a.length, b.length));
  }

  static public void arraycopy(Object src, int srcPos, int destPos, int n) {
    arraycopy(src, srcPos, src, destPos, n);
  }

  static public void arraycopy(Object src, int srcPos, Object dest, int destPos, int n) {
    if (n != 0)
      System.arraycopy(src, srcPos, dest, destPos, n);
  }

  static public List map(Iterable l, Object f) {
    return map(f, l);
  }

  static public List map(Object f, Iterable l) {
    List x = emptyList(l);
    if (l != null)
      for (Object o : l) {
        ping();
        x.add(callF(f, o));
      }
    return x;
  }

  static public List map(Map map, Object f) {
    List x = new ArrayList();
    if (map != null)
      for (Object _e : map.entrySet()) {
        ping();
        Map.Entry e = (Map.Entry) _e;
        x.add(callF(f, e.getKey(), e.getValue()));
      }
    return x;
  }

  static public List map(Object f, Object[] l) {
    return map(f, asList(l));
  }

  static public List map(Object[] l, Object f) {
    return map(f, l);
  }

  static public List map(Object f, Map map) {
    return map(map, f);
  }

  static public <A, B> List<B> map(Iterable<A> l, F1<A, B> f) {
    return map(f, l);
  }

  static public <A, B> List<B> map(F1<A, B> f, Iterable<A> l) {
    List x = emptyList(l);
    if (l != null)
      for (A o : l) {
        ping();
        x.add(callF(f, o));
      }
    return x;
  }

  static public <A, B> List<B> map(IF1<A, B> f, Iterable<A> l) {
    return map(l, f);
  }

  static public <A, B> List<B> map(Iterable<A> l, IF1<A, B> f) {
    List x = emptyList(l);
    if (l != null) {
      var it = l.iterator();
      if (it.hasNext()) {
        var pingSource = pingSource();
        do {
          ping(pingSource);
          x.add(f.get(it.next()));
        } while (it.hasNext());
      }
    }
    return x;
  }

  static public <A, B> List<B> map(IF1<A, B> f, A[] l) {
    return map(l, f);
  }

  static public <A, B> List<B> map(A[] l, IF1<A, B> f) {
    List x = emptyList(l);
    if (l != null)
      for (A o : l) {
        ping();
        x.add(f.get(o));
      }
    return x;
  }

  static public <A, B, C> List<C> map(Map<A, B> map, IF2<A, B, C> f) {
    List x = new ArrayList();
    if (map != null)
      for (Map.Entry<A, B> e : map.entrySet()) {
        ping();
        x.add(f.get(e.getKey(), e.getValue()));
      }
    return x;
  }

  static public <A, B> List<A> map(IF1<A, B> f, A data1, A... moreData) {
    List x = emptyList(l(moreData) + 1);
    x.add(f.get(data1));
    if (moreData != null)
      for (A o : moreData) {
        ping();
        x.add(f.get(o));
      }
    return x;
  }

  static public Object pcallFunction(Object f, Object... args) {
    try {
      return callFunction(f, args);
    } catch (Throwable __e) {
      pcallFail(__e);
    }
    return null;
  }

  static public Object callOpt(Object o) {
    return callF(o);
  }

  static public Object callOpt(Object o, String method, Object... args) {
    return callOpt_withVarargs(o, method, args);
  }

  static public ThreadLocal<PCallPolicy> pcallPolicyForThread_tl_tl = new ThreadLocal();

  static public ThreadLocal<PCallPolicy> pcallPolicyForThread_tl() {
    return pcallPolicyForThread_tl_tl;
  }

  static public PCallPolicy defaultPCallPolicy = __1 -> printStackTrace(__1);

  static public PCallPolicy defaultPCallPolicy() {
    return defaultPCallPolicy;
  }

  static public void defaultPCallPolicy_set(PCallPolicy policy) {
    defaultPCallPolicy = policy;
  }

  static public Object getOptDynOnly(DynamicObject o, String field) {
    if (o == null || o.fieldValues == null)
      return null;
    return o.fieldValues.get(field);
  }

  static public String asString(Object o) {
    return o == null ? null : o.toString();
  }

  static public boolean endsWithIgnoreCase(String a, String b) {
    int la = l(a), lb = l(b);
    return la >= lb && regionMatchesIC(a, la - lb, b, 0, lb);
  }

  static public boolean endsWithIgnoreCase(String a, String b, Matches m) {
    if (!endsWithIgnoreCase(a, b))
      return false;
    if (m != null)
      m.m = new String[] { substring(a, 0, l(a) - l(b)) };
    return true;
  }

  static public <A> Set<A> synchroHashSet() {
    return synchronizedSet(new HashSet<A>());
  }

  static public PingSource pingSource() {
    return pingSource_tl().get();
  }

  static public PingSource pingSource(Thread thread) {
    return pingSource_tl().get(thread);
  }

  static public Object callOpt_withVarargs(Object o, String method, Object... args) {
    try {
      if (o == null)
        return null;
      if (o instanceof Class) {
        Class c = (Class) o;
        _MethodCache cache = callOpt_getCache(c);
        Method me = cache.findMethod(method, args);
        if (me == null) {
          return null;
        }
        if ((me.getModifiers() & Modifier.STATIC) == 0)
          return null;
        return invokeMethod(me, null, args);
      } else {
        Class c = o.getClass();
        _MethodCache cache = callOpt_getCache(c);
        Method me = cache.findMethod(method, args);
        if (me != null)
          return invokeMethod(me, o, args);
        List<Method> methods = cache.cache.get(method);
        if (methods != null)
          methodSearch: for (Method m : methods) {
            {
              if (!(m.isVarArgs()))
                continue;
            }
            Object[] newArgs = massageArgsForVarArgsCall(m, args);
            if (newArgs != null)
              return invokeMethod(m, o, newArgs);
          }
        return null;
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public boolean regionMatchesIC(String a, int offsetA, String b, int offsetB, int len) {
    return a != null && a.regionMatches(true, offsetA, b, offsetB, len);
  }

  static public BetterThreadLocal<PingSource> pingSource_tl_var = new BetterThreadLocal<PingSource>() {

    @Override
    public PingSource initialValue() {
      return ping_v3_pingSourceMaker().get();
    }
  };

  static public BetterThreadLocal<PingSource> pingSource_tl() {
    return pingSource_tl_var;
  }

  static public IF0<PingSource> ping_v3_pingSourceMaker_cache;

  static public IF0<PingSource> ping_v3_pingSourceMaker() {
    if (ping_v3_pingSourceMaker_cache == null)
      ping_v3_pingSourceMaker_cache = ping_v3_pingSourceMaker_load();
    return ping_v3_pingSourceMaker_cache;
  }

  static public IF0<PingSource> ping_v3_pingSourceMaker_load() {
    return or((IF0) vm_generalMap_get("ping_v3_pingSourceMaker"), () -> null);
  }

  static abstract public class VF1<A> implements IVF1<A> {

    public abstract void get(A a);
  }

  final static public class _MethodCache {

    final public Class c;

    final public HashMap<String, List<Method>> cache = new HashMap();

    public _MethodCache(Class c) {
      this.c = c;
      _init();
    }

    public void _init() {
      Class _c = c;
      java.lang.Module myModule = getClass().getModule();
      boolean anyHiddenClasses = false;
      while (_c != null) {
        boolean exported = classIsExportedTo(_c, myModule);
        if (!exported)
          anyHiddenClasses = true;
        else
          for (Method m : _c.getDeclaredMethods()) if ((anyHiddenClasses || !isAbstract(m)) && !reflection_isForbiddenMethod(m))
            multiMapPut(cache, m.getName(), makeAccessible(m));
        _c = _c.getSuperclass();
      }
      for (Class intf : allInterfacesImplementedBy(c)) for (Method m : intf.getDeclaredMethods()) if ((anyHiddenClasses || m.isDefault()) && !reflection_isForbiddenMethod(m))
        multiMapPut(cache, m.getName(), makeAccessible(m));
    }

    public Method findMethod(String method, Object[] args) {
      try {
        List<Method> m = cache.get(method);
        if (m == null)
          return null;
        int n = m.size();
        for (int i = 0; i < n; i++) {
          Method me = m.get(i);
          if (call_checkArgs(me, args, false))
            return me;
        }
        return null;
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public Method findStaticMethod(String method, Object[] args) {
      try {
        List<Method> m = cache.get(method);
        if (m == null)
          return null;
        int n = m.size();
        for (int i = 0; i < n; i++) {
          Method me = m.get(i);
          if (isStaticMethod(me) && call_checkArgs(me, args, false))
            return me;
        }
        return null;
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }
  }

  static public class Matches {

    public String[] m;

    public Matches() {
    }

    public Matches(String... m) {
      this.m = m;
    }

    public String get(int i) {
      return i < m.length ? m[i] : null;
    }

    public String unq(int i) {
      return unquote(get(i));
    }

    public String tlc(int i) {
      return unq(i).toLowerCase();
    }

    public boolean bool(int i) {
      return "true".equals(unq(i));
    }

    public String rest() {
      return m[m.length - 1];
    }

    public int psi(int i) {
      return Integer.parseInt(unq(i));
    }

    public String toString() {
      return "Matches(" + joinWithComma(quoteAll(asList(m))) + ")";
    }

    public int hashCode() {
      return _hashCode(toList(m));
    }

    public boolean equals(Object o) {
      return o instanceof Matches && arraysEqual(m, ((Matches) o).m);
    }
  }

  static public class Symbol implements CharSequence {

    public String text;

    public Symbol() {
    }

    public Symbol(String text, boolean dummy) {
      this.text = text;
    }

    public int hashCode() {
      return _hashCode(text);
    }

    public String toString() {
      return text;
    }

    public boolean equals(Object o) {
      return this == o;
    }

    public int length() {
      return text.length();
    }

    public char charAt(int index) {
      return text.charAt(index);
    }

    public CharSequence subSequence(int start, int end) {
      return text.substring(start, end);
    }
  }

  static public class Var<A> implements IVar<A>, ISetter<A> {

    public Var() {
    }

    public Var(A v) {
      this.v = v;
    }

    public A v;

    public synchronized void set(A a) {
      if (v != a) {
        v = a;
        notifyAll();
      }
    }

    public synchronized A get() {
      return v;
    }

    public synchronized boolean has() {
      return v != null;
    }

    public void clear() {
      set(null);
    }

    public synchronized A getAndSet(A a) {
      var value = v;
      set(a);
      return value;
    }

    public IF0<A> getter() {
      return () -> get();
    }

    public IVF1<A> setter() {
      return __40 -> set(__40);
    }

    public String toString() {
      return str(this.get());
    }
  }

  static public class Scorer<A> {

    public double score, total;

    public List<A> successes, errors;

    public boolean verboseFailures, verboseAll;

    final public void add(double score) {
      addZeroToOne(score);
    }

    public void addZeroToOne(double score) {
      ++total;
      this.score += clamp(score, 0, 1);
    }

    public void addZeroToOneError(double error) {
      addZeroToOne(1 - error);
    }

    public void addError() {
      add(false);
    }

    public void addError(A info) {
      add(false, info);
    }

    public void error(A info) {
      addError(info);
    }

    public void addOK() {
      add(true);
    }

    public void addOK(A info) {
      add(true, info);
    }

    public void ok() {
      addOK();
    }

    public void ok(A info) {
      addOK(info);
    }

    public boolean add(boolean correct) {
      ++total;
      if (correct)
        ++score;
      return correct;
    }

    public boolean add(boolean correct, A info) {
      main.add(correct ? successes : errors, info);
      if (verboseAll || verboseFailures && !correct)
        _print((correct ? "[GOOD] " : "[BAD] ") + info);
      return add(correct);
    }

    public void eq(Object a, Object b) {
      if (_eq(a, b))
        add(true);
      else
        add(false, (A) (a + " != " + b));
    }

    public void print() {
      main.print(toString());
    }

    public String toString() {
      return formatDouble(ratioToPercent(score, total), 1) + "% correct (n=" + formatDouble(total, 1) + ")";
    }

    public double get() {
      return ratioToPercent(score, total);
    }

    public double percentScore() {
      return get();
    }

    public double score() {
      return get();
    }

    public boolean allCorrect() {
      return score == total;
    }

    public void add(Scorer scorer) {
      if (scorer == null)
        return;
      total += scorer.total;
      score += scorer.score;
      addAll(successes, scorer.successes);
      addAll(errors, scorer.errors);
    }

    public void collectErrors() {
      errors = new ArrayList();
    }

    public void collectSuccesses() {
      successes = new ArrayList();
    }
  }

  static public class Timestamp implements Comparable<Timestamp>, IFieldsToList {

    public long date;

    public Timestamp(long date) {
      this.date = date;
    }

    public boolean equals(Object o) {
      if (!(o instanceof Timestamp))
        return false;
      Timestamp __1 = (Timestamp) o;
      return date == __1.date;
    }

    public int hashCode() {
      int h = 2059094262;
      h = boostHashCombine(h, _hashCode(date));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { date };
    }

    public Timestamp() {
      date = now();
    }

    public Timestamp(Date date) {
      if (date != null)
        this.date = date.getTime();
    }

    final public long toLong() {
      return unixDate();
    }

    public long unixDate() {
      return date;
    }

    public long unixSeconds() {
      return unixDate() / 1000;
    }

    public String toString() {
      return formatLocalDateWithSeconds(date);
    }

    public int compareTo(Timestamp t) {
      return t == null ? 1 : cmp(date, t.date);
    }

    public Timestamp plus(Seconds seconds) {
      return plus(seconds == null ? null : seconds.getDouble());
    }

    final public Timestamp plusSeconds(double seconds) {
      return plus(seconds);
    }

    public Timestamp plus(double seconds) {
      return new Timestamp(date + toMS(seconds));
    }

    public long minus(Timestamp ts) {
      return unixDate() - ts.unixDate();
    }

    public Timestamp minus(long milliseconds) {
      return new Timestamp(date - milliseconds);
    }

    public long sysTime() {
      return clockTimeToSystemTime(date);
    }

    public Duration minusAsDuration(Timestamp ts) {
      return Duration.ofMillis(minus(ts));
    }
  }

  static public class GazelleV_LeftArrowScriptParser extends SimpleLeftToRightParser {

    final public GazelleV_LeftArrowScriptParser setMagicSwitch(boolean magicSwitch) {
      return magicSwitch(magicSwitch);
    }

    public GazelleV_LeftArrowScriptParser magicSwitch(boolean magicSwitch) {
      this.magicSwitch = magicSwitch;
      return this;
    }

    final public boolean getMagicSwitch() {
      return magicSwitch();
    }

    public boolean magicSwitch() {
      return magicSwitch;
    }

    public boolean magicSwitch = true;

    final public GazelleV_LeftArrowScriptParser setObjectScopesEnabled(boolean objectScopesEnabled) {
      return objectScopesEnabled(objectScopesEnabled);
    }

    public GazelleV_LeftArrowScriptParser objectScopesEnabled(boolean objectScopesEnabled) {
      this.objectScopesEnabled = objectScopesEnabled;
      return this;
    }

    final public boolean getObjectScopesEnabled() {
      return objectScopesEnabled();
    }

    public boolean objectScopesEnabled() {
      return objectScopesEnabled;
    }

    public boolean objectScopesEnabled = true;

    public ClassNameResolver classNameResolver;

    public List functionContainers = new ArrayList();

    final public GazelleV_LeftArrowScriptParser setLasClassLoader(ILASClassLoader lasClassLoader) {
      return lasClassLoader(lasClassLoader);
    }

    public GazelleV_LeftArrowScriptParser lasClassLoader(ILASClassLoader lasClassLoader) {
      this.lasClassLoader = lasClassLoader;
      return this;
    }

    final public ILASClassLoader getLasClassLoader() {
      return lasClassLoader();
    }

    public ILASClassLoader lasClassLoader() {
      return lasClassLoader;
    }

    public ILASClassLoader lasClassLoader;

    final public GazelleV_LeftArrowScriptParser setClassDefPrefix(String classDefPrefix) {
      return classDefPrefix(classDefPrefix);
    }

    public GazelleV_LeftArrowScriptParser classDefPrefix(String classDefPrefix) {
      this.classDefPrefix = classDefPrefix;
      return this;
    }

    final public String getClassDefPrefix() {
      return classDefPrefix();
    }

    public String classDefPrefix() {
      return classDefPrefix;
    }

    public String classDefPrefix;

    final public GazelleV_LeftArrowScriptParser setSourceInfo(Object sourceInfo) {
      return sourceInfo(sourceInfo);
    }

    public GazelleV_LeftArrowScriptParser sourceInfo(Object sourceInfo) {
      this.sourceInfo = sourceInfo;
      return this;
    }

    final public Object getSourceInfo() {
      return sourceInfo();
    }

    public Object sourceInfo() {
      return sourceInfo;
    }

    public Object sourceInfo;

    final public GazelleV_LeftArrowScriptParser setOptimize(boolean optimize) {
      return optimize(optimize);
    }

    public GazelleV_LeftArrowScriptParser optimize(boolean optimize) {
      this.optimize = optimize;
      return this;
    }

    final public boolean getOptimize() {
      return optimize();
    }

    public boolean optimize() {
      return optimize;
    }

    public boolean optimize = true;

    final public GazelleV_LeftArrowScriptParser setUseFixedVarContexts(boolean useFixedVarContexts) {
      return useFixedVarContexts(useFixedVarContexts);
    }

    public GazelleV_LeftArrowScriptParser useFixedVarContexts(boolean useFixedVarContexts) {
      this.useFixedVarContexts = useFixedVarContexts;
      return this;
    }

    final public boolean getUseFixedVarContexts() {
      return useFixedVarContexts();
    }

    public boolean useFixedVarContexts() {
      return useFixedVarContexts;
    }

    public boolean useFixedVarContexts = false;

    public Scope scope;

    public LinkedHashMap<String, LASValueDescriptor> knownVars = new LinkedHashMap();

    public Set<String> closerTokens = litset(";", "}", ")");

    public BuildingScript currentReturnableScript;

    public BuildingScript currentLoop;

    public boolean inParens = false;

    public int idCounter;

    public Map<String, LASClassDef> classDefs = new HashMap();

    final public Map<String, GazelleV_LeftArrowScript.FunctionDef> getFunctionDefs() {
      return functionDefs();
    }

    public Map<String, GazelleV_LeftArrowScript.FunctionDef> functionDefs() {
      return functionDefs;
    }

    public Map<String, GazelleV_LeftArrowScript.FunctionDef> functionDefs = new HashMap();

    public Set<String> flags = ciSet();

    final public GazelleV_LeftArrowScriptParser setInternStringLiterals(boolean internStringLiterals) {
      return internStringLiterals(internStringLiterals);
    }

    public GazelleV_LeftArrowScriptParser internStringLiterals(boolean internStringLiterals) {
      this.internStringLiterals = internStringLiterals;
      return this;
    }

    final public boolean getInternStringLiterals() {
      return internStringLiterals();
    }

    public boolean internStringLiterals() {
      return internStringLiterals;
    }

    public boolean internStringLiterals = true;

    transient public Set<IVF1<Map<String, LASValueDescriptor>>> onKnownVarsSnapshot;

    public GazelleV_LeftArrowScriptParser onKnownVarsSnapshot(IVF1<Map<String, LASValueDescriptor>> f) {
      onKnownVarsSnapshot = createOrAddToSyncLinkedHashSet(onKnownVarsSnapshot, f);
      return this;
    }

    public GazelleV_LeftArrowScriptParser removeKnownVarsSnapshotListener(IVF1<Map<String, LASValueDescriptor>> f) {
      main.remove(onKnownVarsSnapshot, f);
      return this;
    }

    public void knownVarsSnapshot(Map<String, LASValueDescriptor> knownVars) {
      if (onKnownVarsSnapshot != null)
        for (var listener : onKnownVarsSnapshot) pcallF_typed(listener, knownVars);
    }

    transient public Set<IVF1<LASValueDescriptor>> onTypeHook;

    public GazelleV_LeftArrowScriptParser onTypeHook(IVF1<LASValueDescriptor> f) {
      onTypeHook = createOrAddToSyncLinkedHashSet(onTypeHook, f);
      return this;
    }

    public GazelleV_LeftArrowScriptParser removeTypeHookListener(IVF1<LASValueDescriptor> f) {
      main.remove(onTypeHook, f);
      return this;
    }

    public void typeHook(LASValueDescriptor type) {
      if (onTypeHook != null)
        for (var listener : onTypeHook) pcallF_typed(listener, type);
    }

    static public class EvaluableWrapper implements IFieldsToList {

      public GazelleV_LeftArrowScript.Evaluable expr;

      public EvaluableWrapper() {
      }

      public EvaluableWrapper(GazelleV_LeftArrowScript.Evaluable expr) {
        this.expr = expr;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + expr + ")";
      }

      public boolean equals(Object o) {
        if (!(o instanceof EvaluableWrapper))
          return false;
        EvaluableWrapper __21 = (EvaluableWrapper) o;
        return eq(expr, __21.expr);
      }

      public int hashCode() {
        int h = 700525824;
        h = boostHashCombine(h, _hashCode(expr));
        return h;
      }

      public Object[] _fieldsToList() {
        return new Object[] { expr };
      }
    }

    public class BuildingScript {

      public int id = ++idCounter;

      final public BuildingScript setReturnable(boolean returnable) {
        return returnable(returnable);
      }

      public BuildingScript returnable(boolean returnable) {
        this.returnable = returnable;
        return this;
      }

      final public boolean getReturnable() {
        return returnable();
      }

      public boolean returnable() {
        return returnable;
      }

      public boolean returnable = false;

      final public BuildingScript setIsLoopBody(boolean isLoopBody) {
        return isLoopBody(isLoopBody);
      }

      public BuildingScript isLoopBody(boolean isLoopBody) {
        this.isLoopBody = isLoopBody;
        return this;
      }

      final public boolean getIsLoopBody() {
        return isLoopBody();
      }

      public boolean isLoopBody() {
        return isLoopBody;
      }

      public boolean isLoopBody = false;

      public BuildingScript returnableParent, loopParent;

      public GazelleV_LeftArrowScript.Script script = new GazelleV_LeftArrowScript.Script();

      public List<GazelleV_LeftArrowScript.Evaluable> steps = new ArrayList();

      public Map<String, GazelleV_LeftArrowScript.FunctionDef> functionDefs = new HashMap();

      public BuildingScript(boolean returnable) {
        this();
        this.returnable = returnable;
      }

      public BuildingScript(boolean returnable, boolean isLoopBody) {
        this();
        this.isLoopBody = isLoopBody;
        this.returnable = returnable;
      }

      public BuildingScript() {
      }

      public void add(GazelleV_LeftArrowScript.Evaluable step) {
        if (step != null)
          steps.add(step);
      }

      public GazelleV_LeftArrowScript.Evaluable get() {
        var lastStep = last(steps);
        if (lastStep instanceof GazelleV_LeftArrowScript.ReturnFromScript)
          if (((GazelleV_LeftArrowScript.ReturnFromScript) lastStep).script == script)
            replaceLast(steps, ((GazelleV_LeftArrowScript.ReturnFromScript) lastStep).value);
        if (!returnable && l(steps) == 1 && empty(functionDefs))
          return first(steps);
        if (nempty(functionDefs))
          script.functionDefs = functionDefs;
        script.steps = toTypedArray(GazelleV_LeftArrowScript.Evaluable.class, steps);
        return script;
      }

      public String toStringLong() {
        return pnlToLines(steps);
      }

      public String toString() {
        return formatRecordVars("BuildingScript", "id", id, "returnable", returnable, "returnableParent", returnableParent, "script", script);
      }
    }

    {
      tokenize = text -> {
        List<String> tok = tokenize_base(text);
        tok = preprocess(tok);
        if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
          print("=== PREPROCESSED\n" + indentx(join(tok)) + "\n===");
        return tok;
      };
    }

    public List<String> preprocess(List<String> tok) {
      tok = tokenIndexedList3(tok);
      tok_ifdef(tok, __20 -> getFlag(__20));
      tok_pcall_script(tok);
      tok_script_settable(tok);
      jreplace(tok, "LS", "L<S>");
      jreplace(tok, ", +<id>", ", $3 $3");
      return cloneList(tok);
    }

    public GazelleV_LeftArrowScript.Script parse(String text) {
      setText(text);
      init();
      GazelleV_LeftArrowScript.Script script = parse();
      if (!atEnd())
        throw fail("End of script expected");
      return script;
    }

    public GazelleV_LeftArrowScript.Script parse() {
      GazelleV_LeftArrowScript.Script script = parseReturnableScript();
      if (optimize)
        script = script.optimizeScript();
      return script;
    }

    public GazelleV_LeftArrowScript.Script parseReturnableScript() {
      return (GazelleV_LeftArrowScript.Script) parseScript(new BuildingScript().returnable(true));
    }

    public GazelleV_LeftArrowScript.Evaluable parseScript(BuildingScript script) {
      return linkToSrc(() -> {
        script.returnableParent = currentReturnableScript;
        script.loopParent = currentLoop;
        if (script.returnable)
          currentReturnableScript = script;
        if (script.isLoopBody)
          currentLoop = script;
        return parseBuildingScript(script);
      });
    }

    public GazelleV_LeftArrowScript.Evaluable parseBuildingScript(BuildingScript script) {
      try {
        parseScript_2(script);
        var builtScript = script.get();
        currentReturnableScript = script.returnableParent;
        currentLoop = script.loopParent;
        return builtScript;
      } catch (Throwable e) {
        if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
          print("Parsed so far:\n" + script);
        throw rethrowAndAppendToMessage(e, squareBracketed(spaceCombine(sourceInfo, lineAndColumn(-1))));
      }
    }

    public void parseScript_2(BuildingScript script) {
      AutoCloseable __10 = tempRestoreMap(knownVars);
      try {
        AssureAdvance assure = new AssureAdvance();
        while (assure.get()) {
          knownVarsSnapshot(knownVars);
          if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
            print("parseScript_2: Next token is " + quote(token()));
          if (is(";")) {
            next();
            continue;
          }
          if (isOneOf("}", ")"))
            break;
          GazelleV_LeftArrowScript.Evaluable instruction = linkToSrc(() -> parseInstruction(script));
          if (instruction != null)
            script.add(instruction);
        }
        if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
          print("parseScript_2 done");
        knownVarsSnapshot(knownVars);
      } finally {
        _close(__10);
      }
    }

    public GazelleV_LeftArrowScript.Evaluable parseParamDecl(BuildingScript script) {
      String var = assertIdentifier(tpp());
      LASValueDescriptor valueDescriptor = new LASValueDescriptor();
      if (is(":")) {
        var type = parseColonType();
        valueDescriptor = LASValueDescriptor.nonExactCanBeNull(type);
      }
      knownVars.put(var, valueDescriptor);
      script.script.params = putOrCreateLinkedHashMap(script.script.params, var, valueDescriptor);
      return null;
    }

    public GazelleV_LeftArrowScript.Evaluable parseInstruction(BuildingScript script) {
      if (consumeOpt("param"))
        return parseParamDecl(script);
      if (is("throw")) {
        consume();
        return new GazelleV_LeftArrowScript.Throw(parseExpr());
      }
      if (isOneOf("return", "ret")) {
        consume();
        GazelleV_LeftArrowScript.Evaluable expr;
        if (atCmdEnd())
          expr = _const(null);
        else
          expr = parseAssignmentOrExpr();
        return new GazelleV_LeftArrowScript.ReturnFromScript(currentReturnableScript.script, expr);
      }
      if (consumeOpt("continue")) {
        assertCmdEnd();
        if (currentLoop == null)
          throw fail("continue outside of loop");
        return new GazelleV_LeftArrowScript.Continue(currentLoop.script);
      }
      if (is("temp")) {
        consume();
        GazelleV_LeftArrowScript.Evaluable tempExpr = parseAssignmentOrExpr();
        if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
          print("tempExpr", tempExpr);
        GazelleV_LeftArrowScript.Evaluable body = parseScript(new BuildingScript());
        if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
          print("body", body);
        return new GazelleV_LeftArrowScript.TempBlock(tempExpr, body);
      }
      if (is("will") && is(1, "return")) {
        consume(2);
        GazelleV_LeftArrowScript.Evaluable exp = parseAssignmentOrExpr();
        GazelleV_LeftArrowScript.Evaluable body = parseScript(new BuildingScript());
        return new GazelleV_LeftArrowScript.WillReturn(exp, body);
      }
      if (is("var") && isIdentifier(token(1))) {
        consume();
        String varName = consumeIdentifier();
        var type = parseColonTypeOpt();
        if (consumeLeftArrowOpt()) {
          if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
            print("Found assignment");
          GazelleV_LeftArrowScript.Evaluable rhs = parseExpr();
          assertNotNull("Expression expected", rhs);
          knownVars.put(varName, type == null ? or(rhs.returnType(), new LASValueDescriptor()) : LASValueDescriptor.nonExactCanBeNull(type));
          return new GazelleV_LeftArrowScript.VarDeclaration(varName, typeToClass(type), rhs);
        }
      }
      return parseAssignmentOrExpr();
    }

    public GazelleV_LeftArrowScript.Evaluable parseAssignmentOrExpr() {
      {
        var __2 = parseAssignmentOpt();
        if (__2 != null)
          return __2;
      }
      return parseExpr();
    }

    public GazelleV_LeftArrowScript.Evaluable parseAssignmentOpt() {
      String t = token();
      if (isIdentifier(t)) {
        Type type = null;
        var ptr = ptr();
        if (is(1, ":")) {
          next();
          type = parseColonType();
          unconsume();
        }
        if (is(1, "<") && is(2, "-")) {
          if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
            print("Found assignment");
          next(3);
          GazelleV_LeftArrowScript.Evaluable rhs = parseExpr();
          assertNotNull("Expression expected", rhs);
          boolean newVar = !knownVars.containsKey(t);
          if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
            printVars("newVar", newVar, "t", t, "knownVars", knownVars);
          if (newVar && scope != null) {
            var __3 = scope.completeAssignmentOpt(t, rhs);
            if (__3 != null)
              return __3;
          }
          knownVars.put(t, type == null ? or(rhs.returnType(), new LASValueDescriptor()) : LASValueDescriptor.nonExactCanBeNull(type));
          return newVar ? new GazelleV_LeftArrowScript.VarDeclaration(t, null, rhs) : new GazelleV_LeftArrowScript.Assignment(t, rhs);
        }
        ptr(ptr);
      }
      return null;
    }

    public GazelleV_LeftArrowScript.Evaluable parseOptionalInnerExpression() {
      if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
        printVars("parseOptionalInnerExpression", "token", token());
      if (atCmdEnd() || isOneOf("{", ",", "then"))
        return null;
      return parseInnerExpr();
    }

    public GazelleV_LeftArrowScript.Evaluable _const(Object o) {
      return new GazelleV_LeftArrowScript.Const(o);
    }

    public GazelleV_LeftArrowScript.Evaluable parseInnerExpr() {
      return parseExpr(true);
    }

    public GazelleV_LeftArrowScript.Evaluable parseExpr() {
      if (metaGet("scaffolding") != null)
        scaffoldCalled(this, "parseExpr");
      return parseExpr(false);
    }

    public GazelleV_LeftArrowScript.Evaluable parseExpr(boolean inner) {
      GazelleV_LeftArrowScript.Evaluable e = linkToSrc(() -> inner ? parseExpr_impl(true) : parseFullExpr());
      if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
        print("parseExpr done:\n" + GazelleV_LeftArrowScript.indentedScriptStruct(e));
      return e;
    }

    public GazelleV_LeftArrowScript.Evaluable parseFullExpr() {
      return linkToSrc(() -> parseExprPlusOptionalComma());
    }

    public GazelleV_LeftArrowScript.CallOnTarget parseThenCall(GazelleV_LeftArrowScript.CallOnTarget expr) {
      consume("then");
      GazelleV_LeftArrowScript.CallOnTarget secondCall = (GazelleV_LeftArrowScript.CallOnTarget) (parseCall(null));
      return new GazelleV_LeftArrowScript.Then(expr, secondCall);
    }

    public GazelleV_LeftArrowScript.Evaluable parseExprPlusOptionalComma() {
      GazelleV_LeftArrowScript.Evaluable expr = parseExpr_impl(false);
      while (consumeOpt(",")) {
        expr = parseCall_noCmdEndCheck(expr);
      }
      return expr;
    }

    public GazelleV_LeftArrowScript.Evaluable parseExpr_impl(boolean inner) {
      if (atEnd())
        return null;
      String t = token();
      if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
        printVars("parseExpr", "token", t);
      if (is(";"))
        return null;
      if (consumeOpt("try")) {
        GazelleV_LeftArrowScript.Evaluable body = parseCurlyBlock();
        while (consumeOpt("catch")) {
          String var = consumeIdentifierOpt();
          AutoCloseable __11 = tempAddKnownVars(var);
          try {
            GazelleV_LeftArrowScript.Evaluable catchBlock = parseCurlyBlock();
            body = new GazelleV_LeftArrowScript.TryCatch(body, var, catchBlock);
          } finally {
            _close(__11);
          }
        }
        if (consumeOpt("finally")) {
          GazelleV_LeftArrowScript.Evaluable finallyBlock = parseCurlyBlock();
          return new GazelleV_LeftArrowScript.TryFinally(body, finallyBlock);
        }
        return body;
      }
      if (is("def")) {
        var fd = parseFunctionDefinition(currentReturnableScript.functionDefs);
        return _const(fd);
      }
      if (is("{"))
        return parseCurlyBlock();
      if (is("-") && empty(nextSpace()) && startsWithDigit(token(1)) || startsWithDigit(t)) {
        var e = parseNumberLiteral();
        return parseCall(inner, e);
      }
      if (isQuoted(t)) {
        var e = parseStringLiteral();
        return parseCall(inner, e);
      }
      if (startsWith(t, '\'')) {
        consume();
        var e = _const(first(unquote(t)));
        return parseCall(inner, e);
      }
      if (isIdentifier(t)) {
        if (consumeOpt("synchronized")) {
          var target = parseExpr();
          var body = parseCurlyBlock();
          return new GazelleV_LeftArrowScript.Synchronized(target, body);
        }
        if (is("while"))
          return parseWhileLoop();
        if (is("do"))
          return parseDoWhileLoop();
        if (is("for"))
          return parseForEach();
        if (is("if"))
          return parseIfStatement();
        if (is("repeat"))
          return parseRepeatStatement();
        if (is("outer")) {
          consume();
          var a = parseAssignmentOpt();
          if (!(a instanceof GazelleV_LeftArrowScript.Assignment))
            throw fail("Assignment expected");
          return new GazelleV_LeftArrowScript.AssignmentToOuterVar(((GazelleV_LeftArrowScript.Assignment) a).var, ((GazelleV_LeftArrowScript.Assignment) a).expression);
        }
        consume();
        if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
          print("Consumed identifier " + t + ", next token: " + token() + ", inner: " + inner);
        var e = parseExprStartingWithIdentifier(t, inner);
        return inner ? parseCall(inner, e) : e;
      }
      if (eq(t, "(")) {
        consume();
        GazelleV_LeftArrowScript.Evaluable e = parseExpr_inParens();
        consume(")");
        return parseCall(inner, e);
      }
      if (isOneOf("&", "|") && empty(nextSpace()) && is(1, token())) {
        return parseBinaryOperator();
      }
      throw fail("Identifier, literal, operator or opening parentheses expected (got: " + quote(t));
    }

    public GazelleV_LeftArrowScript.Evaluable parseExpr_inParens() {
      boolean inParensOld = inParens;
      inParens = true;
      try {
        return parseExpr();
      } finally {
        inParens = inParensOld;
      }
    }

    public GazelleV_LeftArrowScript.Evaluable parseNumberLiteral() {
      String t = consumeMultiTokenLiteral();
      if (swic(t, "0x"))
        return _const(parseHexInt(dropFirst(t, 2)));
      if (swic(t, "0b"))
        return _const(intFromBinary(dropFirst(t, 2)));
      if (isInteger(t))
        return _const(parseIntOrLong(t));
      if (ewic(t, "f"))
        return _const(parseFloat(t));
      if (endsWith(t, "L"))
        return _const(parseLong(t));
      return _const(parseDouble(t));
    }

    public GazelleV_LeftArrowScript.Evaluable parseBinaryOperator() {
      boolean and = is("&");
      next(2);
      GazelleV_LeftArrowScript.Evaluable a = parseInnerExpr();
      GazelleV_LeftArrowScript.Evaluable b = parseInnerExpr();
      return and ? new GazelleV_LeftArrowScript.BoolAnd(a, b) : new GazelleV_LeftArrowScript.BoolOr(a, b);
    }

    public boolean qualifiedNameContinues() {
      return empty(prevSpace()) && eq(token(), ".") && empty(nextSpace()) && isIdentifier(token(1));
    }

    public GazelleV_LeftArrowScript.Evaluable parseNewExpr() {
      String className = assertIdentifier(tpp());
      parseTypeArguments(null);
      LASClassDef cd = classDefs.get(className);
      if (cd != null)
        return new GazelleV_LeftArrowScript.NewObject_LASClass(cd.resolvable(lasClassLoader));
      var type = knownVars.get(className);
      if (type != null)
        return new GazelleV_LeftArrowScript.NewObject_UnknownClass(new GazelleV_LeftArrowScript.GetVar(className), parseArguments());
      Object o = findExternalObject(className);
      if (o instanceof Class) {
        Class c = (Class) o;
        if (c == List.class)
          c = ArrayList.class;
        else if (c == Map.class)
          c = HashMap.class;
        else if (c == Set.class)
          c = HashSet.class;
        return new GazelleV_LeftArrowScript.NewObject(c, parseArguments());
      }
      throw new ClassNotFound(className);
    }

    public GazelleV_LeftArrowScript.Evaluable parseExprStartingWithIdentifier(String t, boolean inner) {
      if (eq(t, "true"))
        return _const(true);
      if (eq(t, "false"))
        return _const(false);
      if (eq(t, "null"))
        return _const(null);
      if (eq(t, "new"))
        return parseNewExpr();
      if (eq(t, "class")) {
        unconsume();
        return new GazelleV_LeftArrowScript.ClassDef(parseClassDef().resolvable(lasClassLoader));
      }
      if (eq(t, "list") && is("{")) {
        GazelleV_LeftArrowScript.Script block = parseReturnableCurlyBlock();
        GazelleV_LeftArrowScript.Evaluable e = new GazelleV_LeftArrowScript.ListFromScript(block);
        return parseCall(inner, e);
      }
      var type = knownVars.get(t);
      if (type != null) {
        var e = new GazelleV_LeftArrowScript.GetVar(t).returnType(type);
        if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
          print("Found var acccess: " + e + ", " + (!inner ? "Checking for call" : "Returning expression"));
        return parseCall(inner, e);
      }
      if (scope != null) {
        {
          var __4 = scope.parseExprStartingWithIdentifierOpt(t, inner);
          if (__4 != null)
            return __4;
        }
      }
      LASClassDef cd = classDefs.get(t);
      if (cd != null)
        return new GazelleV_LeftArrowScript.ClassDef(cd.resolvable(lasClassLoader));
      if (!inner) {
        var fdef = lookupFunction(t);
        if (fdef != null) {
          if (is("~"))
            return parseTildeCall(new GazelleV_LeftArrowScript.CallFunction(fdef, new GazelleV_LeftArrowScript.Evaluable[0]));
          return new GazelleV_LeftArrowScript.CallFunction(fdef, parseArguments());
        }
      }
      if (eq(t, "_context"))
        return new GazelleV_LeftArrowScript.GetVarContext();
      Object o = findExternalObject(t);
      var start = ptr().minus(2);
      if (o == null) {
        throw new UnknownObject(t);
      } else if (o instanceof EvaluableWrapper) {
        return parseCall(inner, ((EvaluableWrapper) o).expr);
      } else if (inner)
        return linkToSrc(start, _const(o));
      else if (o instanceof Class) {
        return parseExprStartingWithClass(start, ((Class) o));
      } else if (o instanceof MethodOnObject) {
        if (inner)
          throw fail("Can't call methods in arguments");
        return new GazelleV_LeftArrowScript.CallMethod(_const(((MethodOnObject) o).object), ((MethodOnObject) o).method, parseArguments());
      } else
        return parseCall(_const(o));
    }

    public GazelleV_LeftArrowScript.Evaluable parseExprStartingWithClass(ListAndIndex<String> start, Class c) {
      if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
        printVars("parseExprStartingWithClass", "c", c);
      if (atCmdEnd())
        return linkToSrc(start, _const(c));
      if (is("("))
        return new GazelleV_LeftArrowScript.NewObject(c, parseArguments());
      {
        var __5 = parseLambdaOpt(c);
        if (__5 != null)
          return __5;
      }
      if (isIdentifier()) {
        String name = tpp();
        if (hasStaticMethodNamed(c, name))
          return new GazelleV_LeftArrowScript.CallMethod(_const(c), name, parseArguments());
        if (isInterface(c))
          return parseLambdaMethodRef(c, name);
        var field = getField(c, name);
        if (field == null)
          field = findFieldInInterfaces(c, name);
        if (field != null) {
          if (!isStaticField(field))
            throw fail(field + " is not a static field");
          if (consumeLeftArrowOpt()) {
            GazelleV_LeftArrowScript.Evaluable rhs = parseExpr();
            return new GazelleV_LeftArrowScript.SetStaticField(field, rhs);
          }
          assertCmdEnd();
          return new GazelleV_LeftArrowScript.GetStaticField(field);
        }
        throw fail(name + " not found in " + c + " (looked for method or field)");
      } else
        throw fail("Method name expected: " + token());
    }

    public GazelleV_LeftArrowScript.Evaluable parseLambdaOpt(Class c) {
      if (!isInterface(c))
        return null;
      var ptr = ptr();
      var parameterized = parseTypeArgumentsOpt(c);
      if (is("{"))
        return finishLambdaDef(c, null);
      if (consumeOpt("_")) {
        String methodName = consumeIdentifier();
        return new GazelleV_LeftArrowScript.LambdaMethodOnArgument(c, methodName, parseArguments());
      }
      LinkedHashMap<String, LASValueDescriptor> args = new LinkedHashMap();
      while (isIdentifier()) {
        String name = consumeIdentifier();
        args.put(name, typeToValueDescriptor(parseColonTypeOpt()));
      }
      if (!(is("-") && is(1, ">"))) {
        ptr(ptr);
        return null;
      }
      skip(2);
      return finishLambdaDef(c, args);
    }

    public GazelleV_LeftArrowScript.Evaluable finishLambdaDef(Class c, LinkedHashMap<String, LASValueDescriptor> args) {
      AutoCloseable __12 = tempAddKnownVars(args);
      try {
        knownVarsSnapshot(knownVars);
        GazelleV_LeftArrowScript.Evaluable body;
        if (is("{"))
          body = parseReturnableCurlyBlock();
        else
          body = parseExpr();
        var lambda = new GazelleV_LeftArrowScript.LambdaDef(c, toStringArray(keys(args)), body);
        if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
          print("finishLambdaDef done:\n" + GazelleV_LeftArrowScript.indentedScriptStruct(lambda));
        return lambda;
      } finally {
        _close(__12);
      }
    }

    public GazelleV_LeftArrowScript.Evaluable parseLambdaMethodRef(Class c, String name) {
      var fdef = lookupFunction(name);
      if (fdef != null) {
        GazelleV_LeftArrowScript.Evaluable[] curriedArguments = parseArguments();
        return new GazelleV_LeftArrowScript.CurriedScriptFunctionLambda(c, fdef, curriedArguments);
      }
      Object function = findExternalObject(name);
      if (function == null)
        throw new UnknownObject(name);
      if (function instanceof MethodOnObject) {
        Object target = ((MethodOnObject) function).object;
        String targetMethod = ((MethodOnObject) function).method;
        GazelleV_LeftArrowScript.Evaluable[] curriedArguments = parseArguments();
        return new GazelleV_LeftArrowScript.CurriedMethodLambda(c, target, targetMethod, curriedArguments);
      } else if (function instanceof Class) {
        Class c2 = (Class) function;
        assertCmdEnd();
        var ctors = constructorsWithNumberOfArguments(c2, 1);
        if (empty(ctors))
          throw fail("No single argument constructor found in " + c2);
        return new GazelleV_LeftArrowScript.CurriedConstructorLambda(c, toArray(Constructor.class, ctors), null);
      } else
        throw fail(function + " is not an instantiable class or callable method");
    }

    public GazelleV_LeftArrowScript.FunctionDef lookupFunction(String name) {
      var script = currentReturnableScript;
      while (script != null) {
        var f = script.functionDefs.get(name);
        if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
          printVars("lookupFunction", "script", script, "name", name, "f", f);
        if (f != null)
          return f;
        script = script.returnableParent;
      }
      return functionDefs.get(name);
    }

    public GazelleV_LeftArrowScript.Evaluable[] parseArguments() {
      List<GazelleV_LeftArrowScript.Evaluable> l = new ArrayList();
      try {
        while (true) {
          if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
            print("parseArgumentsAsList: token is " + quote(t()));
          if (is("+") && empty(nextSpace()) && isIdentifier(token(1))) {
            consume();
            l.add(_const(token()));
            continue;
          }
          if (consumeOpt("<")) {
            GazelleV_LeftArrowScript.Evaluable a = parseFullExpr();
            if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
              print("parseArgumentsAsList: token after expr is " + quote(t()));
            if (a == null)
              throw fail("expression expected");
            l.add(a);
            break;
          }
          GazelleV_LeftArrowScript.Evaluable a = parseOptionalInnerExpression();
          if (a == null)
            break;
          l.add(a);
        }
        return toArrayOrNull(GazelleV_LeftArrowScript.Evaluable.class, l);
      } catch (Throwable _e) {
        if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
          print("Arguments parsed so far: " + l);
        throw rethrow(_e);
      }
    }

    public String consumeMultiTokenLiteral() {
      return consumeUntilSpaceOr(() -> atCmdEnd() || is(","));
    }

    public boolean atCmdEnd() {
      return !inParens && atEndOrLineBreak() || closerTokens.contains(token());
    }

    public void assertCmdEnd() {
      if (!atCmdEnd())
        throw fail("Expected end of command, token is: " + quote(token()));
    }

    public GazelleV_LeftArrowScript.Evaluable parseCall(GazelleV_LeftArrowScript.Evaluable target) {
      return parseCall(false, target);
    }

    public GazelleV_LeftArrowScript.Evaluable parseCall(boolean inner, GazelleV_LeftArrowScript.Evaluable target) {
      returnTypeHook(target);
      if (atCmdEnd())
        return target;
      if (inner) {
        {
          var __6 = parseTildeCall(target);
          if (__6 != null)
            return __6;
        }
        return target;
      }
      return parseCall_noCmdEndCheck(target);
    }

    public void returnTypeHook(GazelleV_LeftArrowScript.Evaluable e) {
      if (e != null && e.returnType() != null)
        typeHook(e.returnType());
    }

    public GazelleV_LeftArrowScript.Evaluable parseCall_noCmdEndCheck(GazelleV_LeftArrowScript.Evaluable target) {
      var start = ptr();
      returnTypeHook(target);
      if (isIdentifier()) {
        String name = tpp();
        if (consumeLeftArrowOpt()) {
          GazelleV_LeftArrowScript.Evaluable rhs = parseExpr();
          return new GazelleV_LeftArrowScript.SetField(target, name, rhs);
        }
        boolean allowNullReference = consumeOpt("?");
        var args = parseArguments();
        var call = finalizeCall(start, target, name, args);
        set(call, "allowNullReference", allowNullReference);
        return call;
      }
      {
        var __7 = parseTildeCall(target);
        if (__7 != null)
          return __7;
      }
      if (is("!") && is(1, "_getField_")) {
        next(2);
        String field = consumeIdentifier();
        return new GazelleV_LeftArrowScript.GetField(target, field);
      }
      return target;
    }

    public boolean consumeLeftArrowOpt() {
      if (is("<") && eq(token(1), "-")) {
        next(2);
        return true;
      }
      return false;
    }

    public GazelleV_LeftArrowScript.Evaluable parseTildeCall(GazelleV_LeftArrowScript.Evaluable target) {
      var start = ptr();
      if (consumeOpt("~")) {
        if (isIdentifier()) {
          String key = consumeIdentifier();
          GazelleV_LeftArrowScript.CallOnTarget call = finalizeCall1(start, target, "getOpt", _const(key));
          call.allowNullReference(true);
          return parseCall(finalizeCall2(call));
        } else {
          int idx = consumeInteger();
          return parseCall(finalizeCall(start, target, "listGet", _const(idx)));
        }
      }
      return null;
    }

    public GazelleV_LeftArrowScript.Evaluable finalizeCall(ListAndIndex<String> start, GazelleV_LeftArrowScript.Evaluable target, String name, GazelleV_LeftArrowScript.Evaluable... args) {
      return finalizeCall2(finalizeCall1(start, target, name, args));
    }

    public GazelleV_LeftArrowScript.Evaluable finalizeCall2(GazelleV_LeftArrowScript.CallOnTarget call) {
      while (is("then")) call = parseThenCall(call);
      return call;
    }

    public GazelleV_LeftArrowScript.CallOnTarget finalizeCall1(ListAndIndex<String> start, GazelleV_LeftArrowScript.Evaluable target, String name, GazelleV_LeftArrowScript.Evaluable... args) {
      if (magicSwitch) {
        Object ext = findExternalObject(name);
        if (ext instanceof MethodOnObject) {
          if (nempty(args))
            return new GazelleV_LeftArrowScript.CallMethodOrGlobalFunction(target, name, ((MethodOnObject) ext), args);
          else
            return new GazelleV_LeftArrowScript.CallMethodOrGetFieldOrGlobalFunction(target, name, ((MethodOnObject) ext));
        }
      }
      if (nempty(args))
        return new GazelleV_LeftArrowScript.CallMethod(target, name, args);
      else
        return linkToSrc(start, new GazelleV_LeftArrowScript.CallMethodOrGetField(target, name));
    }

    public <A> A linkToSrc(ListAndIndex<String> start, A a) {
      if (a instanceof IHasTokenRangeWithSrc)
        if (((IHasTokenRangeWithSrc) a).tokenRangeWithSrc() == null)
          ((IHasTokenRangeWithSrc) a).setTokenRangeWithSrc(new TokenRangeWithSrc(start, ptr().plus(-1)).sourceInfo(sourceInfo()));
      return a;
    }

    public <A> A linkToSrc(IF0<A> a) {
      var start = ptr();
      return linkToSrc(start, a.get());
    }

    transient public IF1<String, Object> findExternalObject;

    public Object findExternalObject(String name) {
      return findExternalObject != null ? findExternalObject.get(name) : findExternalObject_base(name);
    }

    final public Object findExternalObject_fallback(IF1<String, Object> _f, String name) {
      return _f != null ? _f.get(name) : findExternalObject_base(name);
    }

    public Object findExternalObject_base(String name) {
      if (eq(name, "void"))
        return void.class;
      {
        var __8 = parsePrimitiveType(name);
        if (__8 != null)
          return __8;
      }
      if (qualifiedNameContinues()) {
        int idx = idx() - 2;
        do next(2); while (qualifiedNameContinues());
        String fqn = joinSubList(tok, idx, idx() - 1);
        return classForName(fqn);
      }
      return findExternalObject2(name);
    }

    transient public IF1<String, Object> findExternalObject2;

    public Object findExternalObject2(String name) {
      return findExternalObject2 != null ? findExternalObject2.get(name) : findExternalObject2_base(name);
    }

    final public Object findExternalObject2_fallback(IF1<String, Object> _f, String name) {
      return _f != null ? _f.get(name) : findExternalObject2_base(name);
    }

    public Object findExternalObject2_base(String name) {
      String fullName = globalClassNames().get(name);
      if (fullName != null)
        return classForName(fullName);
      {
        var __9 = classForNameOpt_noCache(name);
        if (__9 != null)
          return __9;
      }
      for (var container : unnullForIteration(functionContainers)) {
        if (hasMethodNamed(container, name) && !isBlockedFunctionContainerMethod(container, name)) {
          var moo = new MethodOnObject(container, name);
          return moo;
        }
        var field = getField(container, name);
        if (field != null && isStaticField(field))
          return new EvaluableWrapper(new GazelleV_LeftArrowScript.GetStaticField(field));
      }
      return null;
    }

    transient public IF2<Object, String, Boolean> isBlockedFunctionContainerMethod;

    public boolean isBlockedFunctionContainerMethod(Object container, String name) {
      return isBlockedFunctionContainerMethod != null ? isBlockedFunctionContainerMethod.get(container, name) : isBlockedFunctionContainerMethod_base(container, name);
    }

    final public boolean isBlockedFunctionContainerMethod_fallback(IF2<Object, String, Boolean> _f, Object container, String name) {
      return _f != null ? _f.get(container, name) : isBlockedFunctionContainerMethod_base(container, name);
    }

    public boolean isBlockedFunctionContainerMethod_base(Object container, String name) {
      return false;
    }

    public GazelleV_LeftArrowScriptParser allowTheWorld() {
      return allowTheWorld(mc());
    }

    public GazelleV_LeftArrowScriptParser allowTheWorld(Object... functionContainers) {
      for (Object o : unnullForIteration(reversed(functionContainers))) if (!contains(this.functionContainers, o)) {
        this.functionContainers.add(0, o);
        globalClassNames_cache = null;
      }
      return this;
    }

    public void printFunctionDefs(GazelleV_LeftArrowScript.Script script) {
      if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
        print(values(script.functionDefs));
    }

    public AutoCloseable tempAddKnownVars(String... vars) {
      return tempAddKnownVars(nonNulls(vars));
    }

    public AutoCloseable tempAddKnownTypedVars(Iterable<TypedVar> vars) {
      return tempAddKnownVars(mapToMap(vars, v -> pair(v.name, v.type)));
    }

    public AutoCloseable tempAddKnownVars(Iterable<String> vars) {
      var newVars = mapWithSingleValue(vars, new LASValueDescriptor());
      return tempAddKnownVars(newVars);
    }

    public AutoCloseable tempAddKnownVar(String var, LASValueDescriptor type) {
      return tempAddKnownVars(litmap(var, type));
    }

    public AutoCloseable tempAddKnownVars(Map<String, LASValueDescriptor> newVars) {
      return tempMapPutAll(knownVars, newVars);
    }

    public String parseFunctionDefArg(Map<String, LASValueDescriptor> argsOut) {
      if (consumeOpt("(")) {
        String result = parseFunctionDefArg(argsOut);
        consume(")");
        return result;
      }
      if (isIdentifier()) {
        String arg = tpp();
        var type = typeToValueDescriptor(parseColonTypeOpt());
        argsOut.put(arg, type);
        return arg;
      }
      return null;
    }

    public GazelleV_LeftArrowScript.FunctionDef parseFunctionDefinition(Map<String, GazelleV_LeftArrowScript.FunctionDef> functionDefsToAddTo) {
      var start = ptr();
      consume("def");
      boolean synthetic = consumeOpt("!");
      if (synthetic)
        consume("synthetic");
      String functionName = assertIdentifier(tpp());
      if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
        print("parseFunctionDefinition " + functionName);
      return parseFunctionDefinition_step2(functionDefsToAddTo, start, functionName).synthetic(synthetic);
    }

    public GazelleV_LeftArrowScript.FunctionDef parseFunctionDefinition_step2(Map<String, GazelleV_LeftArrowScript.FunctionDef> functionDefsToAddTo, ListAndIndex<String> start, String functionName) {
      LinkedHashMap<String, LASValueDescriptor> args = new LinkedHashMap();
      while (parseFunctionDefArg(args) != null) {
      }
      var returnType = parseColonTypeOpt();
      var fd = new GazelleV_LeftArrowScript.FunctionDef(functionName, keysList(args), null);
      fd.argTypes(valuesArray(LASValueDescriptor.class, args));
      {
        if (functionDefsToAddTo != null)
          functionDefsToAddTo.put(functionName, fd);
      }
      AutoCloseable __13 = tempAddKnownVars(args);
      try {
        if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
          print("Parsing function body");
        fd.body = parseReturnableCurlyBlock();
        if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
          print("Defined function " + functionName + ", added to " + functionDefsToAddTo);
        if (returnType != null)
          fd.returnType(returnType);
        return linkToSrc(start, fd);
      } finally {
        _close(__13);
      }
    }

    public Scope currentScope() {
      return scope;
    }

    public AutoCloseable tempScope(Scope scope) {
      var oldScope = currentScope();
      this.scope = scope;
      return () -> {
        this.scope = oldScope;
      };
    }

    public GazelleV_LeftArrowScript.Script parseReturnableCurlyBlock() {
      return (GazelleV_LeftArrowScript.Script) parseCurlyBlock(new BuildingScript().returnable(true));
    }

    public GazelleV_LeftArrowScript.Evaluable parseCurlyBlock() {
      return parseCurlyBlock(new BuildingScript());
    }

    public GazelleV_LeftArrowScript.Evaluable parseCurlyBlock(BuildingScript script) {
      consume("{");
      boolean inParensOld = inParens;
      inParens = false;
      var body = parseScript(script);
      consume("}");
      inParens = inParensOld;
      return body;
    }

    public GazelleV_LeftArrowScript.Evaluable parseWhileLoop() {
      consume("while");
      var condition = parseExpr();
      var body = parseCurlyBlock(new BuildingScript().isLoopBody(true));
      return new GazelleV_LeftArrowScript.While(condition, body);
    }

    public GazelleV_LeftArrowScript.Evaluable parseDoWhileLoop() {
      consume("do");
      var body = parseCurlyBlock(new BuildingScript().isLoopBody(true));
      consume("while");
      var condition = parseExpr();
      return new GazelleV_LeftArrowScript.DoWhile(condition, body);
    }

    public GazelleV_LeftArrowScript.Evaluable parseForEach() {
      return new ParseForEach().get();
    }

    public class ParseForEach {

      public GazelleV_LeftArrowScript.Evaluable collection, body;

      public IF0<GazelleV_LeftArrowScript.Evaluable> finish;

      public List<TypedVar> vars = new ArrayList();

      public TypedVar addVar(TypedVar var) {
        return addAndReturn(vars, var);
      }

      public TypedVar consumeVar() {
        String name = consumeIdentifier();
        var type = parseColonTypeOpt();
        return addVar(new TypedVar(name, typeToValueDescriptor(type)));
      }

      public void parseBody() {
        AutoCloseable __14 = tempAddKnownTypedVars(vars);
        try {
          body = parseCurlyBlock(new BuildingScript().isLoopBody(true));
        } finally {
          _close(__14);
        }
      }

      public GazelleV_LeftArrowScript.Evaluable get() {
        consume("for");
        if (is(1, "to")) {
          TypedVar var = consumeVar();
          consume("to");
          GazelleV_LeftArrowScript.Evaluable endValue = parseExpr();
          parseBody();
          return new GazelleV_LeftArrowScript.ForIntTo(endValue, var.name, body);
        }
        int iIn = relativeIndexOf("in");
        if (iIn < 0)
          throw fail("for without in");
        if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
          print("iIn", iIn);
        if (iIn == 1 || is(1, ":")) {
          TypedVar var = consumeVar();
          finish = () -> new GazelleV_LeftArrowScript.ForEach(collection, var.name, body).varType(var.type);
        } else if (iIn == 2) {
          if (consumeOpt("iterator")) {
            TypedVar var = consumeVar();
            finish = () -> new GazelleV_LeftArrowScript.ForIterator(collection, var.name, body);
          } else if (consumeOpt("nested")) {
            TypedVar var = consumeVar();
            finish = () -> new GazelleV_LeftArrowScript.ForNested(collection, var.name, body);
          } else
            throw fail("Unknown pattern for 'for' loop");
        } else if (iIn == 3) {
          if (isOneOf("pair", "Pair")) {
            consume();
            TypedVar varA = consumeVar();
            TypedVar varB = consumeVar();
            if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
              printVars("varA", varA, "varB", varB);
            finish = () -> new GazelleV_LeftArrowScript.ForPairs(collection, body, varA.name, varB.name);
          } else {
            TypedVar varA = consumeVar();
            consume(",");
            TypedVar varB = consumeVar();
            finish = () -> new GazelleV_LeftArrowScript.ForKeyValue(collection, body, varA.name, varB.name);
          }
        } else if (iIn == 4) {
          consume("index");
          String varIndex = consumeVar().name;
          consume(",");
          String varElement = consumeVar().name;
          finish = () -> new GazelleV_LeftArrowScript.ForIndex(collection, body, varIndex, varElement);
        } else
          throw fail("Unknown pattern for 'for' loop");
        consume("in");
        collection = parseExpr_inParens();
        if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
          print("collection", collection);
        parseBody();
        return finish.get();
      }
    }

    public GazelleV_LeftArrowScript.Evaluable parseIfStatement() {
      consume("if");
      GazelleV_LeftArrowScript.Evaluable condition, body, elseBranch = null;
      {
        AutoCloseable __15 = tempAdd(closerTokens, "then");
        try {
          condition = parseExpr();
        } finally {
          _close(__15);
        }
      }
      if (consumeOpt("then")) {
        AutoCloseable __16 = tempAdd(closerTokens, "else");
        try {
          body = parseExpr();
          if (consumeOpt("else"))
            elseBranch = parseExpr();
        } finally {
          _close(__16);
        }
      } else {
        body = parseCurlyBlock();
        if (consumeOpt("else")) {
          if (is("if"))
            elseBranch = parseIfStatement();
          else
            elseBranch = parseCurlyBlock();
        }
      }
      return new GazelleV_LeftArrowScript.IfThen(condition, body, elseBranch);
    }

    public GazelleV_LeftArrowScript.Evaluable parseRepeatStatement() {
      consume("repeat");
      var n = parseExpr();
      var body = parseCurlyBlock();
      return new GazelleV_LeftArrowScript.RepeatN(n, body);
    }

    public void addVar(String var) {
      addVar(var, new LASValueDescriptor());
    }

    public void addVar(String var, LASValueDescriptor type) {
      knownVars.put(var, type);
    }

    public void addVar(String var, Class type, boolean canBeNull) {
      addVar(var, typeToValueDescriptor(type, canBeNull));
    }

    public LASValueDescriptor typeToValueDescriptor(Type type) {
      return typeToValueDescriptor(type, true);
    }

    public LASValueDescriptor typeToValueDescriptor(Type type, boolean canBeNull) {
      return type == null ? new LASValueDescriptor() : new LASValueDescriptor.NonExact(typeToClass(type), canBeNull);
    }

    public Map<String, String> globalClassNames_cache;

    public Map<String, String> globalClassNames() {
      if (globalClassNames_cache == null)
        globalClassNames_cache = globalClassNames_load();
      return globalClassNames_cache;
    }

    public Map<String, String> globalClassNames_load() {
      var packages = mapToTreeSet(importedPackages(), pkg -> pkg + ".");
      Map<String, String> out = new HashMap();
      for (var name : unnullForIteration(importedClasses())) out.put(shortenClassName(name), name);
      for (var fc : functionContainers) if (fc instanceof Class) {
        if (isAnonymousClass((Class) fc))
          continue;
        out.put(shortClassName((Class) fc), className((Class) fc));
      }
      var classContainers = classContainerPrefixes();
      TreeSet<String> classContainerSet = asTreeSet(classContainers);
      for (var className : classNameResolver().allFullyQualifiedClassNames()) {
        if (isAnonymousClassName(className))
          continue;
        if (!contains(className, '$')) {
          String pkg = longestPrefixInTreeSet(className, packages);
          if (pkg != null) {
            String shortName = dropPrefix(pkg, className);
            if (!shortName.contains(".") && !out.containsKey(shortName))
              out.put(shortName, className);
          }
        }
        String container = longestPrefixInTreeSet(className, classContainerSet);
        if (container != null) {
          String shortName = dropPrefix(container, className);
          String existing = out.get(shortName);
          if (existing != null) {
            int priority = indexOf(classContainers, container);
            String oldContainer = longestPrefixInTreeSet(existing, classContainerSet);
            int oldPriority = smartIndexOf(classContainers, oldContainer);
            if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
              printVars("className", className, "shortName", shortName, "container", container, "priority", priority, "existing", existing, "oldPriority", oldPriority);
            if (priority > oldPriority)
              continue;
          }
          out.put(shortName, className);
        }
      }
      for (var __23 : _entrySet(unnullForIteration(classShortcuts()))) {
        var key = __23.getKey();
        var val = __23.getValue();
        String fullName = out.get(val);
        if (fullName != null)
          out.put(key, fullName);
      }
      return out;
    }

    transient public IF0<Map<String, String>> classShortcuts;

    public Map<String, String> classShortcuts() {
      return classShortcuts != null ? classShortcuts.get() : classShortcuts_base();
    }

    final public Map<String, String> classShortcuts_fallback(IF0<Map<String, String>> _f) {
      return _f != null ? _f.get() : classShortcuts_base();
    }

    public Map<String, String> classShortcuts_base() {
      return javaxClassShortcuts();
    }

    transient public IF0<Collection<String>> importedPackages;

    public Collection<String> importedPackages() {
      return importedPackages != null ? importedPackages.get() : importedPackages_base();
    }

    final public Collection<String> importedPackages_fallback(IF0<Collection<String>> _f) {
      return _f != null ? _f.get() : importedPackages_base();
    }

    public Collection<String> importedPackages_base() {
      return itemPlus("java.lang", standardImports_fullyImportedPackages());
    }

    transient public IF0<Collection<String>> importedClasses;

    public Collection<String> importedClasses() {
      return importedClasses != null ? importedClasses.get() : importedClasses_base();
    }

    final public Collection<String> importedClasses_fallback(IF0<Collection<String>> _f) {
      return _f != null ? _f.get() : importedClasses_base();
    }

    public Collection<String> importedClasses_base() {
      return standardImports_singleClasses();
    }

    public void addClassAlias(String alias, String longName) {
      String fullName = globalClassNames().get(longName);
      if (fullName != null)
        globalClassNames().put(alias, fullName);
    }

    public List<String> classContainerPrefixes() {
      return map(functionContainers, fc -> className(fc) + "$");
    }

    static public class UnknownObject extends RuntimeException implements IFieldsToList {

      public String name;

      public UnknownObject() {
      }

      public UnknownObject(String name) {
        this.name = name;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + name + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { name };
      }

      public String getMessage() {
        return "Unknown object: " + name;
      }
    }

    static public class ClassNotFound extends UnknownObject {

      public ClassNotFound(String className) {
        super(className);
      }

      public String getMessage() {
        return "Class not found: " + name;
      }
    }

    abstract static public class Scope {

      public GazelleV_LeftArrowScript.Evaluable parseExprStartingWithIdentifierOpt(String t, boolean inner) {
        return null;
      }

      public GazelleV_LeftArrowScript.Evaluable completeAssignmentOpt(String lhs, GazelleV_LeftArrowScript.Evaluable rhs) {
        return null;
      }
    }

    public class ClassDefScope extends Scope implements IFieldsToList {

      public LASClassDef classDef;

      public ClassDefScope() {
      }

      public ClassDefScope(LASClassDef classDef) {
        this.classDef = classDef;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + classDef + ")";
      }

      public boolean equals(Object o) {
        if (!(o instanceof ClassDefScope))
          return false;
        ClassDefScope __0 = (ClassDefScope) o;
        return eq(classDef, __0.classDef);
      }

      public int hashCode() {
        int h = -1760310585;
        h = boostHashCombine(h, _hashCode(classDef));
        return h;
      }

      public Object[] _fieldsToList() {
        return new Object[] { classDef };
      }

      public GazelleV_LeftArrowScript.Evaluable getThis() {
        return new GazelleV_LeftArrowScript.GetVar("this");
      }

      public GazelleV_LeftArrowScript.Evaluable parseExprStartingWithIdentifierOpt(String t, boolean inner) {
        if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
          printVars("ClassDefScope.parseExprStartingWithIdentifierOpt", "t", t);
        return linkToSrc(() -> {
          var field = classDef.fieldsByName.get(t);
          if (field != null) {
            if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
              print("Found field " + field);
            var e = inner ? new GazelleV_LeftArrowScript.GetField(getThis(), field.name) : new GazelleV_LeftArrowScript.CallMethodOrGetField(getThis(), field.name);
            return parseCall(inner, e);
          }
          Class superClass = typeToClass(classDef.superClass);
          var field2 = findFieldOfClass(superClass, t);
          if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
            printVars("field2", field2, "superClass", superClass, "t", t);
          if (field2 != null) {
            if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
              print("Found field " + field2);
            var e = inner ? new GazelleV_LeftArrowScript.GetField(getThis(), field2.getName()) : new GazelleV_LeftArrowScript.CallMethodOrGetField(getThis(), field2.getName());
            return parseCall(inner, e);
          }
          if (!inner && (classDef.methodsByName.containsKey(t) || classHasMethodNamed(typeToClass(classDef.superClass), t))) {
            return new GazelleV_LeftArrowScript.CallMethod(getThis(), t, parseArguments());
          }
          return null;
        });
      }

      public GazelleV_LeftArrowScript.Evaluable completeAssignmentOpt(String lhs, GazelleV_LeftArrowScript.Evaluable rhs) {
        var field = classDef.fieldsByName.get(lhs);
        if (field != null)
          return new GazelleV_LeftArrowScript.SetField(getThis(), lhs, rhs);
        Class superClass = typeToClass(classDef.superClass);
        var field2 = findFieldOfClass(superClass, lhs);
        if (field2 != null)
          return new GazelleV_LeftArrowScript.SetField(getThis(), lhs, rhs);
        return null;
      }
    }

    public LASClassDef parseClassDef() {
      return linkToSrc(() -> {
        consume("class");
        LASClassDef classDef = new LASClassDef();
        classDef.verbose(scaffoldingEnabled());
        if (nempty(classDefPrefix))
          classDef.classDefPrefix(classDefPrefix);
        String name = consumeIdentifier();
        classDef.userGivenName(name);
        classDefs.put(name, classDef);
        AutoCloseable __17 = tempMapPut(classDefs, "selfType", classDef);
        try {
          if (consumeOpt("extends")) {
            classDef.superClass(parseType());
          }
          if (consumeOpt("is"))
            classDef.interfaces.add(parseType());
          consume("{");
          AutoCloseable __18 = tempAddKnownVar("this", new LASValueDescriptor.NonExactType(classDef.resolvable(lasClassLoader), false));
          try {
            AutoCloseable __19 = !objectScopesEnabled ? null : tempScope(new ClassDefScope(classDef));
            try {
              while (!is("}")) {
                if (is(";")) {
                  next();
                  continue;
                }
                if (is("def")) {
                  GazelleV_LeftArrowScript.FunctionDef fd = parseFunctionDefinition(null);
                  if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled())
                    printVars("knownVarsAfterFunctionDef", knownVars);
                  classDef.addMethod(fd);
                  continue;
                }
                var start = ptr();
                if (consumeOpt("ctor")) {
                  var fd = parseFunctionDefinition_step2(null, start, "<init>");
                  fd.returnType(void.class);
                  classDef.addMethod(fd);
                  continue;
                }
                if (consumeOpt("initializer")) {
                  classDef.initializers.add(parseCurlyBlock());
                  continue;
                }
                LASClassDef.FieldDef fd = new LASClassDef.FieldDef();
                while (isOneOf("transient", "static")) {
                  fd.addModifier(tpp());
                }
                fd.name(consumeIdentifier());
                var type = parseColonType();
                fd.type(type);
                if (consumeLeftArrowOpt())
                  fd.initializer(parseExpr());
                classDef.addField(fd);
              }
              consume("}");
              return classDef;
            } finally {
              _close(__19);
            }
          } finally {
            _close(__18);
          }
        } finally {
          _close(__17);
        }
      });
    }

    public Type parseColonTypeOpt() {
      if (is(":"))
        return parseColonType();
      return null;
    }

    public Type parseColonType() {
      consume(":");
      return parseType();
    }

    public Type findType(String typeName) {
      LASClassDef cd = classDefs.get(typeName);
      if (cd != null)
        return cd.resolvable(lasClassLoader);
      Object o = findExternalObject(typeName);
      if (o instanceof Class)
        return ((Class) o);
      throw new ClassNotFound(typeName);
    }

    public Type parseType() {
      String typeName = consumeIdentifier();
      Type type = findType(typeName);
      return parseArrayType(parseTypeArguments(type));
    }

    public Type parseArrayType(Type type) {
      while (is("[") && is(1, "]")) {
        consume(2);
        type = new GenericArrayTypeImpl(type);
      }
      return type;
    }

    final public Type parseTypeArgumentsOpt(Type type) {
      return parseTypeArguments(type);
    }

    public Type parseTypeArguments(Type type) {
      if (is("<") && empty(nextSpace()) && isIdentifier(token(1))) {
        next();
        List<Type> args = new ArrayList();
        while (true) {
          args.add(parseType());
          if (is(">"))
            break;
          consume(",");
        }
        consume(">");
        if (type != null)
          type = new ParameterizedTypeImpl(null, type, toTypedArray(Type.class, args));
      }
      return type;
    }

    transient public IF1<String, Class> classForName;

    public Class classForName(String name) {
      return classForName != null ? classForName.get(name) : classForName_base(name);
    }

    final public Class classForName_fallback(IF1<String, Class> _f, String name) {
      return _f != null ? _f.get(name) : classForName_base(name);
    }

    public Class classForName_base(String name) {
      try {
        return Class.forName(name);
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public void copyFunctionContainersFrom(GazelleV_LeftArrowScriptParser parser) {
      functionContainers = cloneList(parser.functionContainers);
      globalClassNames_cache = parser.globalClassNames();
      isBlockedFunctionContainerMethod = parser.isBlockedFunctionContainerMethod;
    }

    public ClassNameResolver classNameResolver() {
      if (classNameResolver == null)
        classNameResolver = new ClassNameResolver().byteCodePath(assertNotNull(getBytecodePathForClass(this))).init();
      return classNameResolver;
    }

    public GazelleV_LeftArrowScriptParser classNameResolver(ClassNameResolver classNameResolver) {
      this.classNameResolver = classNameResolver;
      return this;
    }

    public GazelleV_LeftArrowScript.Evaluable parseStringLiteral() {
      String s = unquote_relaxedMLS(consume());
      if (internStringLiterals)
        s = intern(s);
      return _const(s);
    }

    public void addFunctionDefs(Map<String, GazelleV_LeftArrowScript.FunctionDef> map) {
      putAll(functionDefs, map);
    }

    public GazelleV_LeftArrowScriptParser setFlag(String flag) {
      flags.add(flag);
      return this;
    }

    public boolean getFlag(String flag) {
      return flags.contains(flag);
    }

    public void noImports() {
      importedPackages = () -> null;
      importedClasses = () -> null;
      classShortcuts = () -> null;
    }

    static public class TypedVar implements IFieldsToList {

      public String name;

      public LASValueDescriptor type;

      public TypedVar() {
      }

      public TypedVar(String name, LASValueDescriptor type) {
        this.type = type;
        this.name = name;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + name + ", " + type + ")";
      }

      public boolean equals(Object o) {
        if (!(o instanceof TypedVar))
          return false;
        TypedVar __22 = (TypedVar) o;
        return eq(name, __22.name) && eq(type, __22.type);
      }

      public int hashCode() {
        int h = -611218819;
        h = boostHashCombine(h, _hashCode(name));
        h = boostHashCombine(h, _hashCode(type));
        return h;
      }

      public Object[] _fieldsToList() {
        return new Object[] { name, type };
      }
    }

    {
      internIdentifiers(true);
    }
  }

  static public class Complex implements IFieldsToList {

    static final public String _fieldOrder = "re im";

    public double re;

    public double im;

    public Complex() {
    }

    public Complex(double re, double im) {
      this.im = im;
      this.re = re;
    }

    public boolean equals(Object o) {
      if (!(o instanceof Complex))
        return false;
      Complex __1 = (Complex) o;
      return re == __1.re && im == __1.im;
    }

    public int hashCode() {
      int h = -1679819632;
      h = boostHashCombine(h, _hashCode(re));
      h = boostHashCombine(h, _hashCode(im));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { re, im };
    }

    public double abs() {
      return sqrt(re * re + im * im);
    }

    public double re() {
      return re;
    }

    public double im() {
      return im;
    }

    final public double angle() {
      return phase();
    }

    public double phase() {
      return Math.atan2(im, re);
    }

    public double fracAngle() {
      return fracNonNeg(angle() / twoPi());
    }

    public String toString() {
      if (im != 0)
        return re == 0 ? im + "i" : re + plusPrefixUnlessMinus(str(im)) + "i";
      else
        return str(re);
    }
  }

  static public class PingSource {

    final public PingSource setAction(IF0<Boolean> action) {
      return action(action);
    }

    public PingSource action(IF0<Boolean> action) {
      this.action = action;
      return this;
    }

    final public IF0<Boolean> getAction() {
      return action();
    }

    public IF0<Boolean> action() {
      return action;
    }

    volatile public IF0<Boolean> action;

    public String text;

    public ThreadPool threadPool;

    public PingSource() {
    }

    public PingSource(ThreadPool threadPool) {
      this.threadPool = threadPool;
    }

    public PingSource(ThreadPool threadPool, String text) {
      this.text = text;
      this.threadPool = threadPool;
    }

    public PingSource(IF0<Boolean> action) {
      this.action = action;
    }

    final public boolean get() {
      var a = action;
      return a != null && a.get();
    }

    final public void ping() {
      var a = action;
      if (a != null)
        a.get();
    }

    public void cancel() {
      action = new Cancelled();
    }

    public class Cancelled implements IF0<Boolean> {

      public Boolean get() {
        throw new PingSourceCancelledException(PingSource.this);
      }
    }

    public class Encapsulated implements Runnable, IFieldsToList {

      public Runnable r;

      public Encapsulated() {
      }

      public Encapsulated(Runnable r) {
        this.r = r;
      }

      public Object[] _fieldsToList() {
        return new Object[] { r };
      }

      public void run() {
        try {
          try {
            pingSource_tl().set(PingSource.this);
            ping();
            r.run();
          } finally {
            pingSource_tl().set(null);
          }
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return PingSource.this + ": " + r;
      }
    }

    public void dO(Runnable r) {
      if (r == null)
        return;
      threadPool.acquireThreadOrQueue(new Encapsulated(r));
    }

    public String toString() {
      String t = text;
      return nempty(t) ? t : super.toString();
    }

    public ISleeper_v2 sleeper() {
      return threadPool.sleeper();
    }
  }

  static public class MultiMap<A, B> implements IMultiMap<A, B> {

    public Map<A, List<B>> data = new HashMap<A, List<B>>();

    public int fullSize;

    public MultiMap() {
    }

    public MultiMap(boolean useTreeMap) {
      if (useTreeMap)
        data = new TreeMap();
    }

    public MultiMap(MultiMap<A, B> map) {
      putAll(map);
    }

    public MultiMap(Map<A, List<B>> data) {
      this.data = data;
    }

    public void put(A key, B value) {
      synchronized (data) {
        List<B> list = data.get(key);
        if (list == null)
          data.put(key, list = _makeEmptyList());
        list.add(value);
        ++fullSize;
      }
    }

    public void add(A key, B value) {
      put(key, value);
    }

    public void addAll(A key, Collection<B> values) {
      putAll(key, values);
    }

    public void addAllIfNotThere(A key, Collection<B> values) {
      synchronized (data) {
        for (B value : values) setPut(key, value);
      }
    }

    public void setPut(A key, B value) {
      synchronized (data) {
        if (!containsPair(key, value))
          put(key, value);
      }
    }

    public boolean containsPair(A key, B value) {
      synchronized (data) {
        return get(key).contains(value);
      }
    }

    public void putAll(Collection<A> keys, B value) {
      synchronized (data) {
        for (A key : unnullForIteration(keys)) put(key, value);
      }
    }

    public void putAll(A key, Collection<B> values) {
      synchronized (data) {
        if (nempty(values))
          getActual(key).addAll(values);
      }
    }

    public void putAll(Iterable<Pair<A, B>> pairs) {
      synchronized (data) {
        for (Pair<A, B> p : unnullForIteration(pairs)) put(p.a, p.b);
      }
    }

    public void removeAll(A key, Collection<B> values) {
      synchronized (data) {
        for (B value : values) remove(key, value);
      }
    }

    public List<B> get(A key) {
      synchronized (data) {
        List<B> list = data.get(key);
        return list == null ? Collections.<B>emptyList() : list;
      }
    }

    public List<B> getOpt(A key) {
      synchronized (data) {
        return data.get(key);
      }
    }

    public List<B> getAndClear(A key) {
      synchronized (data) {
        List<B> l = cloneList(data.get(key));
        remove(key);
        return l;
      }
    }

    public List<B> getActual(A key) {
      synchronized (data) {
        List<B> list = data.get(key);
        if (list == null)
          data.put(key, list = _makeEmptyList());
        return list;
      }
    }

    public void clean(A key) {
      synchronized (data) {
        List<B> list = data.get(key);
        if (list != null && list.isEmpty()) {
          fullSize -= l(list);
          data.remove(key);
        }
      }
    }

    final public Set<A> keys() {
      return keySet();
    }

    public Set<A> keySet() {
      synchronized (data) {
        return data.keySet();
      }
    }

    public void remove(A key) {
      synchronized (data) {
        fullSize -= l(this.getOpt(key));
        data.remove(key);
      }
    }

    final public void remove(Pair<A, B> p) {
      removePair(p);
    }

    public void removePair(Pair<A, B> p) {
      if (p != null)
        remove(p.a, p.b);
    }

    public void remove(A key, B value) {
      synchronized (data) {
        List<B> list = data.get(key);
        if (list != null) {
          if (list.remove(value))
            fullSize--;
          if (list.isEmpty())
            data.remove(key);
        }
      }
    }

    public void clear() {
      synchronized (data) {
        data.clear();
      }
    }

    public boolean containsKey(A key) {
      synchronized (data) {
        return data.containsKey(key);
      }
    }

    public B getFirst(A key) {
      synchronized (data) {
        List<B> list = get(key);
        return list.isEmpty() ? null : list.get(0);
      }
    }

    public void addAll(MultiMap<A, B> map) {
      putAll(map);
    }

    public void putAll(MultiMap<A, B> map) {
      synchronized (data) {
        for (A key : map.keySet()) putAll(key, map.get(key));
      }
    }

    public void putAll(Map<A, B> map) {
      synchronized (data) {
        if (map != null)
          for (Map.Entry<A, B> e : map.entrySet()) put(e.getKey(), e.getValue());
      }
    }

    final public int keyCount() {
      return keysSize();
    }

    public int keysSize() {
      synchronized (data) {
        return l(data);
      }
    }

    final public int fullSize() {
      return size();
    }

    public int size() {
      synchronized (data) {
        return fullSize;
      }
    }

    public List<A> reverseGet(B b) {
      synchronized (data) {
        List<A> l = new ArrayList();
        for (A key : data.keySet()) if (data.get(key).contains(b))
          l.add(key);
        return l;
      }
    }

    public Map<A, List<B>> asMap() {
      synchronized (data) {
        return cloneMap(data);
      }
    }

    public boolean isEmpty() {
      synchronized (data) {
        return data.isEmpty();
      }
    }

    public List<B> _makeEmptyList() {
      return new ArrayList();
    }

    public Collection<List<B>> allLists() {
      synchronized (data) {
        return new ArrayList(data.values());
      }
    }

    public Collection<List<B>> values() {
      return allLists();
    }

    public List<B> allValues() {
      return concatLists(data.values());
    }

    public Object mutex() {
      return data;
    }

    public String toString() {
      return "mm" + str(data);
    }

    public Map<A, List<B>> innerMap() {
      return data;
    }
  }

  static public class SynchronizedSet<E> extends SynchronizedCollection<E> implements Set<E> {

    public SynchronizedSet() {
    }

    public SynchronizedSet(Set<E> s) {
      super(s);
    }

    public SynchronizedSet(Set<E> s, Object mutex) {
      super(s, mutex);
    }

    public boolean equals(Object o) {
      if (this == o)
        return true;
      synchronized (mutex) {
        return c.equals(o);
      }
    }

    public int hashCode() {
      synchronized (mutex) {
        return c.hashCode();
      }
    }
  }

  static public class TestCloseableIterableIterator extends CloseableIterableIterator<Object> {

    public int i;

    transient public Set<Runnable> onClosing;

    public TestCloseableIterableIterator onClosing(Runnable r) {
      onClosing = createOrAddToSyncLinkedHashSet(onClosing, r);
      return this;
    }

    public TestCloseableIterableIterator removeClosingListener(Runnable r) {
      main.remove(onClosing, r);
      return this;
    }

    public void closing() {
      if (onClosing != null)
        for (var listener : onClosing) pcallF_typed(listener);
    }

    public boolean hasNext() {
      return i == 0;
    }

    public Object next() {
      i++;
      return null;
    }

    public void close() {
      closing();
    }
  }

  static public class BetterThreadLocal<A> {

    public Map<Thread, A> map = newWeakHashMap();

    public BetterThreadLocal() {
    }

    public BetterThreadLocal(A value) {
      set(value);
    }

    public boolean isSet() {
      return map.containsKey(currentThread());
    }

    public A get() {
      if (map.containsKey(currentThread()))
        return map.get(currentThread());
      A value = initialValue();
      set(value);
      return value;
    }

    public A get(Thread thread) {
      return thread == null ? null : map.get(thread);
    }

    public void set(A a) {
      map.put(currentThread(), a);
    }

    public A initialValue() {
      return null;
    }
  }

  public interface IntSize {

    public int size();

    default public boolean isEmpty() {
      return size() == 0;
    }
  }

  static public class SynchronizedMap<K, V> implements Map<K, V>, Serializable {

    public SynchronizedMap() {
    }

    public Map<K, V> m;

    public Object mutex;

    public SynchronizedMap(Map<K, V> m) {
      this.m = Objects.requireNonNull(m);
      mutex = this;
    }

    public SynchronizedMap(Map<K, V> m, Object mutex) {
      this.m = m;
      this.mutex = mutex;
    }

    public Map<K, V> innerMap() {
      return m;
    }

    public int size() {
      synchronized (mutex) {
        return m.size();
      }
    }

    public boolean isEmpty() {
      synchronized (mutex) {
        return m.isEmpty();
      }
    }

    public boolean containsKey(Object key) {
      synchronized (mutex) {
        return m.containsKey(key);
      }
    }

    public boolean containsValue(Object value) {
      synchronized (mutex) {
        return m.containsValue(value);
      }
    }

    public V get(Object key) {
      synchronized (mutex) {
        return m.get(key);
      }
    }

    public V put(K key, V value) {
      synchronized (mutex) {
        return m.put(key, value);
      }
    }

    public V remove(Object key) {
      synchronized (mutex) {
        return m.remove(key);
      }
    }

    public void putAll(Map<? extends K, ? extends V> map) {
      synchronized (mutex) {
        m.putAll(map);
      }
    }

    public void clear() {
      synchronized (mutex) {
        m.clear();
      }
    }

    transient public Set<K> keySet;

    transient public Set<Map.Entry<K, V>> entrySet;

    transient public Collection<V> values;

    public Set<K> keySet() {
      synchronized (mutex) {
        if (keySet == null)
          keySet = new SynchronizedSet<>(m.keySet(), mutex);
        return keySet;
      }
    }

    public Set<Map.Entry<K, V>> entrySet() {
      synchronized (mutex) {
        if (entrySet == null)
          entrySet = new SynchronizedSet<>(m.entrySet(), mutex);
        return entrySet;
      }
    }

    public Collection<V> values() {
      synchronized (mutex) {
        if (values == null)
          values = new SynchronizedCollection<>(m.values(), mutex);
        return values;
      }
    }

    public boolean equals(Object o) {
      if (this == o)
        return true;
      synchronized (mutex) {
        return m.equals(o);
      }
    }

    public int hashCode() {
      synchronized (mutex) {
        return m.hashCode();
      }
    }

    public String toString() {
      synchronized (mutex) {
        return m.toString();
      }
    }

    @Override
    public V getOrDefault(Object k, V defaultValue) {
      synchronized (mutex) {
        return m.getOrDefault(k, defaultValue);
      }
    }

    @Override
    public void forEach(BiConsumer<? super K, ? super V> action) {
      synchronized (mutex) {
        m.forEach(action);
      }
    }

    @Override
    public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
      synchronized (mutex) {
        m.replaceAll(function);
      }
    }

    @Override
    public V putIfAbsent(K key, V value) {
      synchronized (mutex) {
        return m.putIfAbsent(key, value);
      }
    }

    @Override
    public boolean remove(Object key, Object value) {
      synchronized (mutex) {
        return m.remove(key, value);
      }
    }

    @Override
    public boolean replace(K key, V oldValue, V newValue) {
      synchronized (mutex) {
        return m.replace(key, oldValue, newValue);
      }
    }

    @Override
    public V replace(K key, V value) {
      synchronized (mutex) {
        return m.replace(key, value);
      }
    }

    @Override
    public V computeIfAbsent(K key, java.util.function.Function<? super K, ? extends V> mappingFunction) {
      synchronized (mutex) {
        return m.computeIfAbsent(key, mappingFunction);
      }
    }

    @Override
    public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
      synchronized (mutex) {
        return m.computeIfPresent(key, remappingFunction);
      }
    }

    @Override
    public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
      synchronized (mutex) {
        return m.compute(key, remappingFunction);
      }
    }

    @Override
    public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
      synchronized (mutex) {
        return m.merge(key, value, remappingFunction);
      }
    }

    @java.io.Serial
    private void writeObject(ObjectOutputStream s) throws IOException {
      synchronized (mutex) {
        s.defaultWriteObject();
      }
    }
  }

  static final public class WeakHasherMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {

    public Hasher hasher = null;

    private boolean keyEquals(Object k1, Object k2) {
      return (hasher == null ? k1.equals(k2) : hasher.equals(k1, k2));
    }

    private int keyHashCode(Object k1) {
      return (hasher == null ? k1.hashCode() : hasher.hashCode(k1));
    }

    private WeakKey WeakKeyCreate(K k) {
      if (k == null)
        return null;
      else
        return new WeakKey(k);
    }

    private WeakKey WeakKeyCreate(K k, ReferenceQueue<? super K> q) {
      if (k == null)
        return null;
      else
        return new WeakKey(k, q);
    }

    final public class WeakKey extends WeakReference<K> {

      public int hash;

      public WeakKey(K k) {
        super(k);
        hash = keyHashCode(k);
      }

      private WeakKey create(K k) {
        if (k == null)
          return null;
        else
          return new WeakKey(k);
      }

      public WeakKey(K k, ReferenceQueue<? super K> q) {
        super(k, q);
        hash = keyHashCode(k);
      }

      private WeakKey create(K k, ReferenceQueue<? super K> q) {
        if (k == null)
          return null;
        else
          return new WeakKey(k, q);
      }

      @Override
      public boolean equals(Object o) {
        if (o == null)
          return false;
        if (this == o)
          return true;
        if (!(o.getClass().equals(WeakKey.class)))
          return false;
        Object t = this.get();
        @SuppressWarnings("unchecked")
        Object u = ((WeakKey) o).get();
        if ((t == null) || (u == null))
          return false;
        if (t == u)
          return true;
        return keyEquals(t, u);
      }

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

    public HashMap<WeakKey, V> hash;

    public ReferenceQueue<? super K> queue = new ReferenceQueue<K>();

    @SuppressWarnings("unchecked")
    private void processQueue() {
      WeakKey wk;
      while ((wk = (WeakKey) queue.poll()) != null) {
        hash.remove(wk);
      }
    }

    public WeakHasherMap(int initialCapacity, float loadFactor) {
      hash = new HashMap<WeakKey, V>(initialCapacity, loadFactor);
    }

    public WeakHasherMap(int initialCapacity) {
      hash = new HashMap<WeakKey, V>(initialCapacity);
    }

    public WeakHasherMap() {
      hash = new HashMap<WeakKey, V>();
    }

    public WeakHasherMap(Hasher h) {
      hash = new HashMap<WeakKey, V>();
      hasher = h;
    }

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

    @Override
    public boolean isEmpty() {
      return entrySet().isEmpty();
    }

    @Override
    public boolean containsKey(Object key) {
      @SuppressWarnings("unchecked")
      K kkey = (K) key;
      return hash.containsKey(WeakKeyCreate(kkey));
    }

    @Override
    public V get(Object key) {
      @SuppressWarnings("unchecked")
      K kkey = (K) key;
      return hash.get(WeakKeyCreate(kkey));
    }

    @Override
    public V put(K key, V value) {
      processQueue();
      return hash.put(WeakKeyCreate(key, queue), value);
    }

    @Override
    public V remove(Object key) {
      processQueue();
      @SuppressWarnings("unchecked")
      K kkey = (K) key;
      return hash.remove(WeakKeyCreate(kkey));
    }

    @Override
    public void clear() {
      processQueue();
      hash.clear();
    }

    @SuppressWarnings("TypeParameterShadowing")
    final public class Entry<K, V> implements Map.Entry<K, V> {

      public Map.Entry<WeakKey, V> ent;

      public K key;

      public Entry(Map.Entry<WeakKey, V> ent, K key) {
        this.ent = ent;
        this.key = key;
      }

      @Override
      public K getKey() {
        return key;
      }

      @Override
      public V getValue() {
        return ent.getValue();
      }

      @Override
      public V setValue(V value) {
        return ent.setValue(value);
      }

      private boolean keyvalEquals(K o1, K o2) {
        return (o1 == null) ? (o2 == null) : keyEquals(o1, o2);
      }

      private boolean valEquals(V o1, V o2) {
        return (o1 == null) ? (o2 == null) : o1.equals(o2);
      }

      @SuppressWarnings("NonOverridingEquals")
      public boolean equals(Map.Entry<K, V> e) {
        return (keyvalEquals(key, e.getKey()) && valEquals(getValue(), e.getValue()));
      }

      @Override
      public int hashCode() {
        V v;
        return (((key == null) ? 0 : keyHashCode(key)) ^ (((v = getValue()) == null) ? 0 : v.hashCode()));
      }
    }

    final public class EntrySet extends AbstractSet<Map.Entry<K, V>> {

      public Set<Map.Entry<WeakKey, V>> hashEntrySet = hash.entrySet();

      @Override
      public Iterator<Map.Entry<K, V>> iterator() {
        return new Iterator<Map.Entry<K, V>>() {

          public Iterator<Map.Entry<WeakKey, V>> hashIterator = hashEntrySet.iterator();

          public Map.Entry<K, V> next = null;

          @Override
          public boolean hasNext() {
            while (hashIterator.hasNext()) {
              Map.Entry<WeakKey, V> ent = hashIterator.next();
              WeakKey wk = ent.getKey();
              K k = null;
              if ((wk != null) && ((k = wk.get()) == null)) {
                continue;
              }
              next = new Entry<K, V>(ent, k);
              return true;
            }
            return false;
          }

          @Override
          public Map.Entry<K, V> next() {
            if ((next == null) && !hasNext())
              throw new NoSuchElementException();
            Map.Entry<K, V> e = next;
            next = null;
            return e;
          }

          @Override
          public void remove() {
            hashIterator.remove();
          }
        };
      }

      @Override
      public boolean isEmpty() {
        return !(iterator().hasNext());
      }

      @Override
      public int size() {
        int j = 0;
        for (Iterator<Map.Entry<K, V>> i = iterator(); i.hasNext(); i.next()) j++;
        return j;
      }

      @Override
      public boolean remove(Object o) {
        processQueue();
        if (!(o instanceof Map.Entry<?, ?>))
          return false;
        @SuppressWarnings("unchecked")
        Map.Entry<K, V> e = (Map.Entry<K, V>) o;
        Object ev = e.getValue();
        WeakKey wk = WeakKeyCreate(e.getKey());
        Object hv = hash.get(wk);
        if ((hv == null) ? ((ev == null) && hash.containsKey(wk)) : hv.equals(ev)) {
          hash.remove(wk);
          return true;
        }
        return false;
      }

      @Override
      public int hashCode() {
        int h = 0;
        for (Iterator<Map.Entry<WeakKey, V>> i = hashEntrySet.iterator(); i.hasNext(); ) {
          Map.Entry<WeakKey, V> ent = i.next();
          WeakKey wk = ent.getKey();
          Object v;
          if (wk == null)
            continue;
          h += (wk.hashCode() ^ (((v = ent.getValue()) == null) ? 0 : v.hashCode()));
        }
        return h;
      }
    }

    public Set<Map.Entry<K, V>> entrySet = null;

    @Override
    public Set<Map.Entry<K, V>> entrySet() {
      if (entrySet == null)
        entrySet = new EntrySet();
      return entrySet;
    }

    public K findKey(Object key) {
      processQueue();
      K kkey = (K) key;
      WeakKey wkey = WeakKeyCreate(kkey);
      WeakKey found = hashMap_findKey(hash, wkey);
      return found == null ? null : found.get();
    }
  }

  static public class CompactLinkedHashSet<A> extends AbstractSet<A> {

    public UnsynchronizedCompactHashSet<Entry<A>> entries = new UnsynchronizedCompactHashSet();

    public Entry<A> head, tail;

    static public class Entry<A> {

      public A value;

      public Entry<A> prev, next;

      public int hashCode() {
        return _hashCode(value);
      }

      public boolean equals(Object o) {
        return o == this || eq(value, o);
      }
    }

    public boolean add(A a) {
      if (entries.contains(a))
        return false;
      Entry<A> n = new Entry();
      n.value = a;
      n.prev = tail;
      if (tail != null)
        tail.next = n;
      tail = n;
      if (head == null)
        head = n;
      entries.add(n);
      return true;
    }

    public boolean remove(Object a) {
      return remove(entries.find(a));
    }

    public boolean remove(Entry<A> node) {
      if (node == null)
        return false;
      if (node.next != null)
        node.next.prev = node.prev;
      else
        tail = node.prev;
      if (node.prev != null)
        node.prev.next = node.next;
      else
        head = node.next;
      entries.remove(node);
      return true;
    }

    public int size() {
      return entries.size();
    }

    public IterableIterator<A> iterator() {
      return new IterableIterator<A>() {

        public Entry<A> entry = head, prev = null;

        public boolean hasNext() {
          return entry != null;
        }

        public A next() {
          A a = entry.value;
          prev = entry;
          entry = entry.next;
          return a;
        }

        public void remove() {
          if (prev == null)
            throw new IllegalStateException();
          CompactLinkedHashSet.this.remove(prev);
          prev = null;
        }
      };
    }

    public void clear() {
      entries.clear();
      head = tail = null;
    }

    public boolean contains(Object a) {
      return entries.contains(a);
    }

    public A find(Object o) {
      Entry<A> e = entries.find(o);
      return e == null ? null : e.value;
    }

    public A prevElement(A a) {
      Entry<A> e = entries.find(a);
      if (e == null || e.prev == null)
        return null;
      return e.prev.value;
    }

    public A nextElement(A a) {
      Entry<A> e = entries.find(a);
      if (e == null || e.next == null)
        return null;
      return e.next.value;
    }

    public A first() {
      return head == null ? null : head.value;
    }

    public A last() {
      return tail == null ? null : tail.value;
    }

    public boolean removeIfSame(Object o) {
      A value = find(o);
      if (value == o) {
        remove(value);
        return true;
      }
      return false;
    }
  }

  static public class Pair<A, B> implements Comparable<Pair<A, B>> {

    final public Pair<A, B> setA(A a) {
      return a(a);
    }

    public Pair<A, B> a(A a) {
      this.a = a;
      return this;
    }

    final public A getA() {
      return a();
    }

    public A a() {
      return a;
    }

    public A a;

    final public Pair<A, B> setB(B b) {
      return b(b);
    }

    public Pair<A, B> b(B b) {
      this.b = b;
      return this;
    }

    final public B getB() {
      return b();
    }

    public B b() {
      return b;
    }

    public B b;

    public Pair() {
    }

    public Pair(A a, B b) {
      this.b = b;
      this.a = a;
    }

    public int hashCode() {
      return hashCodeFor(a) + 2 * hashCodeFor(b);
    }

    public boolean equals(Object o) {
      if (o == this)
        return true;
      if (!(o instanceof Pair))
        return false;
      Pair t = (Pair) o;
      return eq(a, t.a) && eq(b, t.b);
    }

    public String toString() {
      return "<" + a + ", " + b + ">";
    }

    public int compareTo(Pair<A, B> p) {
      if (p == null)
        return 1;
      int i = ((Comparable<A>) a).compareTo(p.a);
      if (i != 0)
        return i;
      return ((Comparable<B>) b).compareTo(p.b);
    }
  }

  static public class Fail extends RuntimeException implements IFieldsToList {

    public Object[] objects;

    public Fail() {
    }

    public Fail(Object... objects) {
      this.objects = objects;
    }

    public Object[] _fieldsToList() {
      return new Object[] { objects };
    }

    public Fail(Throwable cause, Object... objects) {
      super(cause);
      this.objects = objects;
    }

    public String toString() {
      return joinNemptiesWithColon("Fail", getMessage());
    }

    public String getMessage() {
      return commaCombine(getCause(), objects);
    }
  }

  static public class Rect implements WidthAndHeight, IFieldsToList {

    static final public String _fieldOrder = "x y w h";

    public int x;

    public int y;

    public int w;

    public int h;

    public Rect() {
    }

    public Rect(int x, int y, int w, int h) {
      this.h = h;
      this.w = w;
      this.y = y;
      this.x = x;
    }

    public boolean equals(Object o) {
      if (!(o instanceof Rect))
        return false;
      Rect __1 = (Rect) o;
      return x == __1.x && y == __1.y && w == __1.w && h == __1.h;
    }

    public int hashCode() {
      int h = 2543108;
      h = boostHashCombine(h, _hashCode(x));
      h = boostHashCombine(h, _hashCode(y));
      h = boostHashCombine(h, _hashCode(w));
      h = boostHashCombine(h, _hashCode(h));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { x, y, w, h };
    }

    public Rect(Rectangle r) {
      x = r.x;
      y = r.y;
      w = r.width;
      h = r.height;
    }

    public Rect(Pt p, int w, int h) {
      this.h = h;
      this.w = w;
      x = p.x;
      y = p.y;
    }

    public Rect(Rect r) {
      x = r.x;
      y = r.y;
      w = r.w;
      h = r.h;
    }

    final public Rectangle getRectangle() {
      return new Rectangle(x, y, w, h);
    }

    public String toString() {
      return x + "," + y + " / " + w + "," + h;
    }

    final public int x1() {
      return x;
    }

    final public int y1() {
      return y;
    }

    final public int x2() {
      return x + w;
    }

    final public int y2() {
      return y + h;
    }

    final public boolean contains(Pt p) {
      return contains(p.x, p.y);
    }

    final public boolean contains(int _x, int _y) {
      return _x >= x && _y >= y && _x < x + w && _y < y + h;
    }

    final public boolean contains(Rectangle r) {
      return rectContains(this, r);
    }

    final public boolean empty() {
      return w <= 0 || h <= 0;
    }

    final public int getWidth() {
      return w;
    }

    final public int getHeight() {
      return h;
    }

    final public int area() {
      return w * h;
    }

    final public int x() {
      return x;
    }

    final public int y() {
      return y;
    }

    public WidthAndHeight widthAndHeight() {
      return main.widthAndHeight(w, h);
    }
  }

  static public class Pt implements Comparable<Pt>, IDoublePt {

    public int x, y;

    public Pt() {
    }

    public Pt(Point p) {
      x = p.x;
      y = p.y;
    }

    public Pt(int x, int y) {
      this.y = y;
      this.x = x;
    }

    public Point getPoint() {
      return new Point(x, y);
    }

    public boolean equals(Object o) {
      return o instanceof Pt && x == ((Pt) o).x && y == ((Pt) o).y;
    }

    public int hashCode() {
      return boostHashCombine(x, y);
    }

    public int compareTo(Pt p) {
      if (y != p.y)
        return cmp(y, p.y);
      return cmp(x, p.x);
    }

    public String toString() {
      return x + ", " + y;
    }

    public double length() {
      return sqrt(x * x + y * y);
    }

    public Pt minus(Pt p) {
      return ptMinus(this, p);
    }

    public double x_double() {
      return x;
    }

    public double y_double() {
      return y;
    }
  }

  static public class BufferedImageWithMeta extends BufferedImage implements IMeta {

    public BufferedImageWithMeta(ColorModel cm, WritableRaster raster, boolean isRasterPremultiplied, Hashtable<?, ?> properties) {
      super(cm, raster, isRasterPremultiplied, properties);
    }

    volatile public Object meta;

    public void _setMeta(Object meta) {
      this.meta = meta;
    }

    public Object _getMeta() {
      return meta;
    }

    final public boolean scaffolding() {
      return scaffoldingEnabled();
    }

    final public boolean scaffolded() {
      return scaffoldingEnabled();
    }

    public boolean scaffoldingEnabled() {
      return main.scaffoldingEnabled(this);
    }

    public boolean scaffoldingEnabled(Object o) {
      return main.scaffoldingEnabled(o);
    }

    public String toString_base() {
      return super.toString();
    }

    public String toString() {
      Object o = metaGet("toString", this);
      if (o instanceof String)
        return ((String) o);
      if (o instanceof IF1)
        return str(((IF1) o).get(this));
      return toString_base();
    }
  }

  static abstract public class F0<A> {

    abstract public A get();
  }

  static abstract public class F1<A, B> {

    abstract public B get(A a);
  }

  static abstract public class IterableIterator<A> implements Iterator<A>, Iterable<A> {

    public Iterator<A> iterator() {
      return this;
    }

    public void remove() {
      unsupportedOperation();
    }
  }

  static public class RGB {

    public float r, g, b;

    public RGB() {
    }

    public RGB(float brightness) {
      r = g = b = brightness;
    }

    public RGB(float r, float g, float b) {
      this.r = r;
      this.g = g;
      this.b = b;
    }

    public RGB(double r, double g, double b) {
      this.r = (float) r;
      this.g = (float) g;
      this.b = (float) b;
    }

    public RGB(double[] rgb) {
      this(rgb[0], rgb[1], rgb[2]);
    }

    public RGB(int rgb) {
      r = rgbRed(rgb) / 255f;
      g = rgbGreen(rgb) / 255f;
      b = rgbBlue(rgb) / 255f;
    }

    public RGB(double brightness) {
      this((float) brightness);
    }

    public RGB(Color color) {
      r = color.getRed() / 255f;
      g = color.getGreen() / 255f;
      b = color.getBlue() / 255f;
    }

    public RGB(String hex) {
      int i = l(hex) - 6;
      r = Integer.parseInt(hex.substring(i, i + 2), 16) / 255f;
      g = Integer.parseInt(hex.substring(i + 2, i + 4), 16) / 255f;
      b = Integer.parseInt(hex.substring(i + 4, i + 6), 16) / 255f;
    }

    public float getComponent(int i) {
      return i == 0 ? r : i == 1 ? g : b;
    }

    public int getInt(int i) {
      return i == 0 ? redInt() : i == 1 ? greenInt() : blueInt();
    }

    public Color getColor() {
      return new Color(clampZeroToOne(r), clampZeroToOne(g), clampZeroToOne(b));
    }

    public static RGB newSafe(float r, float g, float b) {
      return new RGB(Math.max(0, Math.min(1, r)), Math.max(0, Math.min(1, g)), Math.max(0, Math.min(1, b)));
    }

    public int asInt() {
      return getColor().getRGB() & 0xFFFFFF;
    }

    public int getInt() {
      return getColor().getRGB() & 0xFFFFFF;
    }

    public int asIntWithAlpha() {
      return rgbInt(redInt(), greenInt(), blueInt()) | 0xFF000000;
    }

    final public float brightness() {
      return getBrightness();
    }

    public float getBrightness() {
      return (r + g + b) / 3.0f;
    }

    public String getHexString() {
      return Integer.toHexString(asInt() | 0xFF000000).substring(2).toUpperCase();
    }

    @Override
    public boolean equals(Object o) {
      if (this == o)
        return true;
      if (!(o instanceof RGB))
        return false;
      RGB rgb = (RGB) o;
      if (Float.compare(rgb.b, b) != 0)
        return false;
      if (Float.compare(rgb.g, g) != 0)
        return false;
      if (Float.compare(rgb.r, r) != 0)
        return false;
      return true;
    }

    @Override
    public int hashCode() {
      int result = (r != +0.0f ? Float.floatToIntBits(r) : 0);
      result = 31 * result + (g != +0.0f ? Float.floatToIntBits(g) : 0);
      result = 31 * result + (b != +0.0f ? Float.floatToIntBits(b) : 0);
      return result;
    }

    public boolean isBlack() {
      return r == 0f && g == 0f && b == 0f;
    }

    public boolean isWhite() {
      return r == 1f && g == 1f && b == 1f;
    }

    public boolean isGrayscale() {
      return r == g && g == b;
    }

    public String toString() {
      return isGrayscale() ? formatChannel(r) : spaceCombine("RGB", formatChannel(r), formatChannel(g), formatChannel(b));
    }

    public String formatChannel(float value) {
      return formatDouble(value, 2);
    }

    public int redInt() {
      return iround(r * 255);
    }

    public int greenInt() {
      return iround(g * 255);
    }

    public int blueInt() {
      return iround(b * 255);
    }

    static public float brightnessToFloat(int brightness) {
      return brightness / 255f;
    }

    public RGB cloneMe() {
      return new RGB(r, g, b);
    }
  }

  public interface PCallPolicy {

    public void handlePcallFail(Throwable e);
  }

  public static interface IF0<A> {

    public A get();
  }

  static public interface Hasher<A> {

    public int hashCode(A a);

    public boolean equals(A a, A b);
  }

  static public interface IFieldsToList {

    public Object[] _fieldsToList();
  }

  static public interface IF2<A, B, C> {

    public C get(A a, B b);
  }

  static public interface IF1<A, B> {

    public B get(A a);
  }

  static public class PersistableThrowable extends DynamicObject {

    public String className;

    public String msg;

    public String stacktrace;

    final public PersistableThrowable setActualThrowable(Throwable actualThrowable) {
      return actualThrowable(actualThrowable);
    }

    public PersistableThrowable actualThrowable(Throwable actualThrowable) {
      this.actualThrowable = actualThrowable;
      return this;
    }

    final public Throwable getActualThrowable() {
      return actualThrowable();
    }

    public Throwable actualThrowable() {
      return actualThrowable;
    }

    transient public Throwable actualThrowable;

    public PersistableThrowable() {
    }

    public PersistableThrowable(Throwable e) {
      actualThrowable = e;
      if (e == null)
        className = "Crazy Null Error";
      else {
        className = getClassName(e).replace('/', '.');
        msg = e.getMessage();
        stacktrace = getStackTrace_noRecord(e);
      }
    }

    public String toString() {
      return nempty(msg) ? className + ": " + msg : className;
    }

    public RuntimeException asRuntimeException() {
      if (actualThrowable != null)
        return main.asRuntimeException(actualThrowable);
      return new Fail(this);
    }
  }

  static public interface IVF1<A> {

    public void get(A a);
  }

  static public class GazelleV_LeftArrowScript {

    abstract static public class Base extends HasTokenRangeWithSrc {

      public RuntimeException rethrowWithSrc(Throwable e) {
        return rethrowWithSrc("", e);
      }

      public RuntimeException rethrowWithSrc(String msg, Throwable e) {
        if (tokenRangeWithSrc() != null)
          throw new ScriptError(tokenRangeWithSrc(), msg, e);
        else
          throw rethrow(e);
      }

      public String indented() {
        return indentedScriptStruct(this);
      }

      public String shortenedSrc() {
        return shortenSrc(tokenRangeWithSrc());
      }
    }

    static public class ScriptError extends RuntimeException implements IHasTokenRangeWithSrc {

      public ScriptError(TokenRangeWithSrc src, Throwable reason) {
        super(reason);
        tokenRangeWithSrc(src);
      }

      public ScriptError(TokenRangeWithSrc src, String msg, Throwable reason) {
        super(msg, reason);
        tokenRangeWithSrc(src);
      }

      final public ScriptError setTokenRangeWithSrc(TokenRangeWithSrc tokenRangeWithSrc) {
        return tokenRangeWithSrc(tokenRangeWithSrc);
      }

      public ScriptError tokenRangeWithSrc(TokenRangeWithSrc tokenRangeWithSrc) {
        this.tokenRangeWithSrc = tokenRangeWithSrc;
        return this;
      }

      final public TokenRangeWithSrc getTokenRangeWithSrc() {
        return tokenRangeWithSrc();
      }

      public TokenRangeWithSrc tokenRangeWithSrc() {
        return tokenRangeWithSrc;
      }

      public TokenRangeWithSrc tokenRangeWithSrc;

      public TokenRangeWithSrc src() {
        return tokenRangeWithSrc();
      }

      public String toString() {
        return super.toString() + "\n  " + src() + " {{ " + shortenSrc(src()) + " }}";
      }
    }

    static public String shortenSrc(TokenRangeWithSrc src) {
      return src == null ? null : shorten(nlToSpace(src.text()));
    }

    public interface Evaluable extends IF0, IHasTokenRangeWithSrc {

      public default Object get() {
        return get(new FlexibleVarContext());
      }

      public Object get(VarContext ctx);

      public default LASValueDescriptor returnType() {
        return null;
      }

      public default Evaluable optimize() {
        return this;
      }

      public default Evaluable optimizeForReturnValueNotNeeded() {
        return this;
      }

      public default Object getWithParams(Object... params) {
        return get(flexibleVarContextFromParams(params));
      }
    }

    abstract static public class EvaluableBase extends Base implements Evaluable {

      final public EvaluableBase setReturnType(LASValueDescriptor returnType) {
        return returnType(returnType);
      }

      public EvaluableBase returnType(LASValueDescriptor returnType) {
        this.returnType = returnType;
        return this;
      }

      final public LASValueDescriptor getReturnType() {
        return returnType();
      }

      public LASValueDescriptor returnType() {
        return returnType;
      }

      public LASValueDescriptor returnType;

      public boolean returnValueNeeded = true;

      public Evaluable optimizeForReturnValueNotNeeded() {
        returnValueNeeded = false;
        return optimize();
      }
    }

    static public AtomicLong scriptIDCounter = new AtomicLong();

    static public long scriptID() {
      return incAtomicLong(scriptIDCounter);
    }

    static public class ListFromScript extends EvaluableBase implements IFieldsToList {

      public Script script;

      public ListFromScript() {
      }

      public ListFromScript(Script script) {
        this.script = script;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + script + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { script };
      }

      public Object get(VarContext ctx) {
        return script.getAsList(ctx);
      }
    }

    static public class Script extends EvaluableBase {

      transient public long id = scriptID();

      public Map<String, FunctionDef> functionDefs;

      public Evaluable[] steps;

      public Map<String, LASValueDescriptor> params;

      public Script() {
      }

      public Script(List<Evaluable> steps) {
        this.steps = toTypedArray(Evaluable.class, steps);
      }

      public Object get(VarContext ctx) {
        Object result = null;
        var pingSource = pingSource();
        for (var step : steps) {
          ping(pingSource);
          result = step.get(ctx);
          var exiting = ctx.exitFromScript;
          if (exiting != null) {
            if (exiting == this) {
              ctx.exitFromScript = null;
              result = ctx.returnValue;
              ctx.returnValue(null);
              return result;
            }
            return null;
          }
        }
        return result;
      }

      public Object getAsList(VarContext ctx) {
        Object result = null;
        var pingSource = pingSource();
        List list = new ArrayList();
        for (var step : steps) {
          ping(pingSource);
          list.add(step.get(ctx));
          var exiting = ctx.exitFromScript;
          if (exiting != null) {
            if (exiting == this) {
              ctx.exitFromScript = null;
              result = ctx.returnValue;
              ctx.returnValue(null);
              list.add(result);
              return list;
            }
            return null;
          }
        }
        return list;
      }

      public String toStringLong() {
        return pnlToLines(steps);
      }

      public String toString() {
        return "Script " + n2(id);
      }

      public FunctionDef getFunction(String name) {
        return mapGet(functionDefs, name);
      }

      final public Script optimize() {
        return optimizeScript();
      }

      public Script optimizeScript() {
        int n = returnValueNeeded ? steps.length - 1 : steps.length;
        for (int i = 0; i < n; i++) steps[i] = steps[i].optimizeForReturnValueNotNeeded();
        for (var f : values(functionDefs)) f.optimize();
        return this;
      }
    }

    static public class FunctionDef extends Base implements IFieldsToList {

      public String name;

      public String[] args;

      public Script body;

      public FunctionDef() {
      }

      public FunctionDef(String name, String[] args, Script body) {
        this.body = body;
        this.args = args;
        this.name = name;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + name + ", " + args + ", " + body + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { name, args, body };
      }

      final public FunctionDef setReturnType(Type returnType) {
        return returnType(returnType);
      }

      public FunctionDef returnType(Type returnType) {
        this.returnType = returnType;
        return this;
      }

      final public Type getReturnType() {
        return returnType();
      }

      public Type returnType() {
        return returnType;
      }

      public Type returnType = Object.class;

      final public FunctionDef setArgTypes(LASValueDescriptor[] argTypes) {
        return argTypes(argTypes);
      }

      public FunctionDef argTypes(LASValueDescriptor[] argTypes) {
        this.argTypes = argTypes;
        return this;
      }

      final public LASValueDescriptor[] getArgTypes() {
        return argTypes();
      }

      public LASValueDescriptor[] argTypes() {
        return argTypes;
      }

      public LASValueDescriptor[] argTypes;

      final public FunctionDef setSynthetic(boolean synthetic) {
        return synthetic(synthetic);
      }

      public FunctionDef synthetic(boolean synthetic) {
        this.synthetic = synthetic;
        return this;
      }

      final public boolean getSynthetic() {
        return synthetic();
      }

      public boolean synthetic() {
        return synthetic;
      }

      public boolean synthetic = false;

      public FunctionDef(String name, List<String> args, Script body) {
        this.args = toStringArray(args);
        this.body = body;
        this.name = name;
      }

      public Object callFromOutside(Object... args) {
        return call(new FlexibleVarContext(), args);
      }

      public Object call(VarContext ctx, Object... args) {
        VarContext ctx2 = new FlexibleVarContext(ctx);
        int n = min(l(args), l(this.args));
        for (int i = 0; i < n; i++) ctx2.put(this.args[i], args[i]);
        return body.get(ctx2);
      }

      public void optimize() {
        body = body.optimize();
      }

      public boolean isConstructor() {
        return eq(name, "<init>");
      }
    }

    static public class Assignment extends EvaluableBase implements IFieldsToList {

      public String var;

      public Evaluable expression;

      public Assignment() {
      }

      public Assignment(String var, Evaluable expression) {
        this.expression = expression;
        this.var = var;
      }

      public Object[] _fieldsToList() {
        return new Object[] { var, expression };
      }

      public Object get(VarContext ctx) {
        Object o = expression.get(ctx);
        ctx.set(var, o);
        return o;
      }

      public String toString() {
        return var + " <- " + expression;
      }
    }

    static public class VarDeclaration extends EvaluableBase implements IFieldsToList {

      public String var;

      public Class type;

      public Evaluable expression;

      public VarDeclaration() {
      }

      public VarDeclaration(String var, Class type, Evaluable expression) {
        this.expression = expression;
        this.type = type;
        this.var = var;
      }

      public Object[] _fieldsToList() {
        return new Object[] { var, type, expression };
      }

      public Object get(VarContext ctx) {
        try {
          Object o = expression == null ? null : expression.get(ctx);
          ctx.set(var, o);
          return o;
        } catch (Throwable e) {
          throw rethrowWithSrc(e);
        }
      }

      public String toString() {
        return "var " + var + " <- " + expression;
      }
    }

    static public class AssignmentToOuterVar extends EvaluableBase implements IFieldsToList {

      public String var;

      public Evaluable expression;

      public AssignmentToOuterVar() {
      }

      public AssignmentToOuterVar(String var, Evaluable expression) {
        this.expression = expression;
        this.var = var;
      }

      public Object[] _fieldsToList() {
        return new Object[] { var, expression };
      }

      public Object get(VarContext ctx) {
        var parent = ctx.parent();
        assertNotNull("No outer variable context", parent);
        Object o = expression.get(ctx);
        parent.set(var, o);
        return o;
      }

      public String toString() {
        return "outer " + var + " <- " + expression;
      }
    }

    static public class NewObject extends EvaluableBase {

      public NewObject() {
      }

      public Class c;

      public Evaluable[] args;

      public NewObject(Class c) {
        this(c, null);
      }

      public NewObject(Class c, Evaluable[] args) {
        this.args = args;
        this.c = c;
        returnType(new LASValueDescriptor.Exact(c, false));
      }

      public Object get(VarContext ctx) {
        try {
          return preciseNuObject(c, evalArgs(args, ctx));
        } catch (Throwable e) {
          throw rethrowWithSrc(e);
        }
      }

      public String toString() {
        return "new " + formatFunctionCall(className(c), args);
      }
    }

    static public class NewObject_LASClass extends NewObject {

      public NewObject_LASClass() {
      }

      public ResolvableLASClass lasClass;

      public NewObject_LASClass(ResolvableLASClass lasClass) {
        this.lasClass = lasClass;
      }

      public NewObject_LASClass(ResolvableLASClass lasClass, Evaluable[] args) {
        this.args = args;
        this.lasClass = lasClass;
      }

      public void resolve() {
        if (c == null)
          c = lasClass.get();
      }

      public Object get(VarContext ctx) {
        resolve();
        return super.get(ctx);
      }

      public String toString() {
        return "new " + formatFunctionCall(str(lasClass), args);
      }
    }

    static public class NewObject_UnknownClass extends NewObject implements IFieldsToList {

      public Evaluable classExpr;

      public Evaluable[] args;

      public NewObject_UnknownClass() {
      }

      public NewObject_UnknownClass(Evaluable classExpr, Evaluable[] args) {
        this.args = args;
        this.classExpr = classExpr;
      }

      public Object[] _fieldsToList() {
        return new Object[] { classExpr, args };
      }

      public Object get(VarContext ctx) {
        try {
          Class c = (Class) (classExpr.get(ctx));
          return preciseNuObject(c, evalArgs(args, ctx));
        } catch (Throwable e) {
          throw rethrowWithSrc(e);
        }
      }

      public String toString() {
        return "new " + formatFunctionCall(classExpr, args);
      }
    }

    static public class CallFunction extends EvaluableBase implements IFieldsToList {

      public FunctionDef f;

      public Evaluable[] args;

      public CallFunction() {
      }

      public CallFunction(FunctionDef f, Evaluable[] args) {
        this.args = args;
        this.f = f;
      }

      public Object[] _fieldsToList() {
        return new Object[] { f, args };
      }

      public Object get(VarContext ctx) {
        var evaledArgs = evalArgs(args, ctx);
        if (ctx.exiting())
          return null;
        return f.call(ctx, evaledArgs);
      }

      public String toString() {
        return formatFunctionCall(f.name, args);
      }

      public LASValueDescriptor returnType() {
        return LASValueDescriptor.fromType(f.getReturnType());
      }
    }

    static public class GetVar extends EvaluableBase implements IFieldsToList {

      public String var;

      public GetVar() {
      }

      public GetVar(String var) {
        this.var = var;
      }

      public Object[] _fieldsToList() {
        return new Object[] { var };
      }

      public Object get(VarContext ctx) {
        return ctx.get(var);
      }

      public String toString() {
        return var;
      }
    }

    static public class Const extends EvaluableBase implements IFieldsToList {

      public Object value;

      public Const() {
      }

      public Const(Object value) {
        this.value = value;
      }

      public Object[] _fieldsToList() {
        return new Object[] { value };
      }

      public Object get(VarContext ctx) {
        return value;
      }

      public String toString() {
        return strOrClassName(value);
      }

      public LASValueDescriptor returnType() {
        return new LASValueDescriptor.KnownValue(value);
      }

      public Object _serialize() {
        boolean ok = isUnproblematicValue(value);
        return ok ? this : toStringWithClass(value);
      }
    }

    static public class GetStaticField extends EvaluableBase implements IFieldsToList {

      public Field field;

      public GetStaticField() {
      }

      public GetStaticField(Field field) {
        this.field = field;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + field + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { field };
      }

      public Object get(VarContext ctx) {
        try {
          return field.get(null);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String _serialize() {
        return str(field);
      }
    }

    abstract static public class CallOnTarget extends EvaluableBase implements IFieldsToList {

      public Evaluable target;

      public CallOnTarget() {
      }

      public CallOnTarget(Evaluable target) {
        this.target = target;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + target + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { target };
      }

      final public CallOnTarget setAllowNullReference(boolean allowNullReference) {
        return allowNullReference(allowNullReference);
      }

      public CallOnTarget allowNullReference(boolean allowNullReference) {
        this.allowNullReference = allowNullReference;
        return this;
      }

      final public boolean getAllowNullReference() {
        return allowNullReference();
      }

      public boolean allowNullReference() {
        return allowNullReference;
      }

      public boolean allowNullReference = false;

      public Object handleNullReference() {
        if (allowNullReference)
          return null;
        else
          throw new NullPointerException();
      }

      abstract public Object evalOnTarget(VarContext ctx, Object object);

      public Object get(VarContext ctx) {
        try {
          Object object = target.get(ctx);
          if (object == null)
            return handleNullReference();
          return evalOnTarget(ctx, object);
        } catch (Throwable e) {
          throw rethrowWithSrc(e);
        }
      }
    }

    static public class CallMethodOrGetField extends CallOnTarget {

      public CallMethodOrGetField() {
      }

      public String name;

      public CallMethodOrGetField(Evaluable target, String name) {
        this.name = name;
        this.target = target;
      }

      public Object evalOnTarget(VarContext ctx, Object object) {
        try {
          return preciseGetOrCallMethod(object, name);
        } catch (Throwable e) {
          throw rethrowWithSrc("Was getting " + name, e);
        }
      }
    }

    static public class GetField extends CallOnTarget {

      public GetField() {
      }

      public String name;

      public GetField(Evaluable target, String name) {
        this.name = name;
        this.target = target;
      }

      public Object evalOnTarget(VarContext ctx, Object object) {
        try {
          return _get(object, name);
        } catch (Throwable e) {
          throw rethrowWithSrc("Was getting " + name, e);
        }
      }
    }

    static public class GetVarContext extends EvaluableBase {

      public Object get(VarContext ctx) {
        return ctx;
      }
    }

    static public class ThrowMethodNotFoundException extends EvaluableBase implements IFieldsToList {

      public CallMethod instruction;

      public ThrowMethodNotFoundException() {
      }

      public ThrowMethodNotFoundException(CallMethod instruction) {
        this.instruction = instruction;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + instruction + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { instruction };
      }

      public Object get(VarContext ctx) {
        throw fail("Method not found: " + instruction);
      }
    }

    static public class ThrowNullPointerException extends EvaluableBase implements IFieldsToList {

      public CallMethod instruction;

      public ThrowNullPointerException() {
      }

      public ThrowNullPointerException(CallMethod instruction) {
        this.instruction = instruction;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + instruction + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { instruction };
      }

      public Object get(VarContext ctx) {
        throw fail("Null pointer exception: " + instruction);
      }
    }

    static public class CallMethod extends CallOnTarget {

      public CallMethod() {
      }

      public String methodName;

      public Evaluable[] args;

      public CallMethod(Evaluable target, String methodName, Evaluable[] args) {
        this.args = args;
        this.methodName = methodName;
        this.target = target;
      }

      public Object evalOnTarget(VarContext ctx, Object object) {
        return newPreciseCall(object, methodName, evalArgs(args, ctx));
      }

      public String toString() {
        return target + "." + formatFunctionCall(methodName, args);
      }

      public Evaluable optimize() {
        var targetType = target.returnType();
        if (targetType != null && targetType.knownValue()) {
          Object o = targetType.value();
          if (o == null)
            return allowNullReference ? _const(null) : new ThrowNullPointerException(this);
          Class[] argTypes = new Class[l(args)];
          for (int i = 0; i < l(args); i++) {
            var type = args[i].returnType();
            if (type == null || !type.javaClassIsExact())
              return this;
            argTypes[i] = type.javaClass();
          }
          List<Method> methods = findMethodsNamed_cached(o, methodName);
          if (any(methods, m -> m.isVarArgs()))
            return this;
          var __4 = findMethod_withPrimitiveWidening_onTypes(o, methodName, argTypes);
          var method = __4.a;
          var widening = __4.b;
          if (method == null)
            return new ThrowMethodNotFoundException(this);
          return new DirectMethodCallOnKnownTarget(widening, o instanceof Class ? null : o, method, args);
        }
        return this;
      }
    }

    static public Object[] evalArgs(Evaluable[] args, VarContext ctx) {
      return mapToArrayOrNull(args, arg -> arg.get(ctx));
    }

    static public class CallMethodOrGlobalFunction extends CallMethod {

      public CallMethodOrGlobalFunction() {
      }

      public MethodOnObject globalFunction;

      static final public Object methodNotFoundSentinel = new Object();

      public CallMethodOrGlobalFunction(Evaluable target, String methodName, MethodOnObject globalFunction, Evaluable[] args) {
        this.args = args;
        this.globalFunction = globalFunction;
        this.methodName = methodName;
        this.target = target;
      }

      public String toString() {
        return target + "." + formatFunctionCall(methodName + "+", args);
      }

      public Object evalOnTarget(VarContext ctx, Object o) {
        Object[] realArgs = evalArgs(args, ctx);
        Object result = newPreciseCall_sentinel(o, methodName, methodNotFoundSentinel, realArgs);
        if (result != methodNotFoundSentinel)
          return result;
        return newPreciseCall(globalFunction.object, globalFunction.method, itemPlusArray(o, realArgs));
      }
    }

    static public class CallMethodOrGetFieldOrGlobalFunction extends CallMethodOrGetField {

      public CallMethodOrGetFieldOrGlobalFunction() {
      }

      public MethodOnObject globalFunction;

      static final public Object notFoundSentinel = new Object();

      public CallMethodOrGetFieldOrGlobalFunction(Evaluable target, String name, MethodOnObject globalFunction) {
        this.globalFunction = globalFunction;
        this.name = name;
        this.target = target;
      }

      public String toString() {
        return target + "." + name + "+";
      }

      public Object evalOnTarget(VarContext ctx, Object o) {
        try {
          Object result = preciseGetOrCallMethod_sentinel(o, name, notFoundSentinel);
          if (result != notFoundSentinel)
            return result;
          return newPreciseCall(globalFunction.object, globalFunction.method, o);
        } catch (Throwable e) {
          throw rethrowWithSrc("Was getting " + name, e);
        }
      }
    }

    abstract static public class LambdaBase extends EvaluableBase implements IFieldsToList {

      public Class intrface;

      public LambdaBase() {
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + intrface + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { intrface };
      }

      transient public Method implementedMethod;

      public LambdaBase(Class intrface) {
        this.intrface = intrface;
        implementedMethod = findSingleInterfaceMethodOrFail(intrface);
      }
    }

    static public class LambdaMethodOnArgument extends LambdaBase {

      public String methodName;

      public Evaluable[] args;

      public LambdaMethodOnArgument(Class intrface, String methodName, Evaluable[] args) {
        super(intrface);
        this.args = args;
        this.methodName = methodName;
      }

      public Object get(VarContext ctx) {
        return proxyFromInvocationHandler(intrface, (proxy, method, actualArgs) -> {
          if (method.getDeclaringClass() == intrface)
            return forwardCall(actualArgs[0], ctx);
          else
            return handleObjectMethodsInProxyInvocationHandler(this, implementedMethod, method, proxy, actualArgs);
        });
      }

      public Object forwardCall(Object target, VarContext ctx) {
        return call(target, methodName, evalArgs(args, ctx));
      }
    }

    static public class LambdaDef extends EvaluableBase implements IFieldsToList {

      public Class intrface;

      public String[] args;

      public Evaluable body;

      public LambdaDef() {
      }

      public Object[] _fieldsToList() {
        return new Object[] { intrface, args, body };
      }

      public Map<String, LASValueDescriptor> argTypes = new HashMap();

      transient public Method implementedMethod;

      public LambdaDef(Class intrface, String[] args, Evaluable body) {
        this.body = body;
        this.args = args;
        this.intrface = intrface;
        implementedMethod = findSingleInterfaceMethodOrFail(intrface);
        if (implementedMethod.getParameterCount() != l(args))
          throw fail("Bad parameter count for lambda: " + implementedMethod + " vs: " + joinWithComma(args));
      }

      public Object get(VarContext ctx) {
        return proxyFromInvocationHandler(intrface, (proxy, method, actualArgs) -> {
          ping();
          if (method.getDeclaringClass() == intrface) {
            var ctx2 = new FlexibleVarContext(ctx);
            var argNames = args;
            for (int i = 0; i < l(args); i++) ctx2.put(argNames[i], actualArgs[i]);
            return body.get(ctx2);
          } else
            return handleObjectMethodsInProxyInvocationHandler(this, implementedMethod, method, proxy, actualArgs);
        });
      }

      public String toString() {
        return "Lambda(" + shortenedSrc() + ")";
      }
    }

    abstract static public class CurriedLambdaBase extends EvaluableBase implements IFieldsToList {

      public Class intrface;

      public Evaluable[] curriedArgs;

      public CurriedLambdaBase() {
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + intrface + ", " + curriedArgs + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { intrface, curriedArgs };
      }

      transient public Method implementedMethod;

      public CurriedLambdaBase(Class intrface, Evaluable[] curriedArgs) {
        this.curriedArgs = curriedArgs;
        this.intrface = intrface;
        implementedMethod = findSingleInterfaceMethodOrFail(intrface);
      }

      public Object get(VarContext ctx) {
        try {
          Object[] curriedArguments = evalArgs(curriedArgs, ctx);
          return proxyFromInvocationHandler(intrface, (proxy, method, actualArgs) -> {
            try {
              if (method.getDeclaringClass() == intrface)
                return forwardCall(ctx, concatMethodArgs(curriedArguments, actualArgs));
              else
                return handleObjectMethodsInProxyInvocationHandler(this, implementedMethod, method, proxy, actualArgs);
            } catch (Throwable e) {
              throw rethrowWithSrc(e);
            }
          });
        } catch (Throwable e) {
          throw rethrowWithSrc(e);
        }
      }

      abstract public Object forwardCall(VarContext ctx, Object[] args);
    }

    static public class CurriedMethodLambda extends CurriedLambdaBase {

      public CurriedMethodLambda() {
      }

      public Object target;

      public String targetMethod;

      public CurriedMethodLambda(Class intrface, Object target, String targetMethod, Evaluable[] curriedArgs) {
        super(intrface, curriedArgs);
        this.targetMethod = targetMethod;
        this.target = target;
      }

      public Object forwardCall(VarContext ctx, Object[] args) {
        return newPreciseCall(target, targetMethod, args);
      }
    }

    static public class CurriedScriptFunctionLambda extends CurriedLambdaBase {

      public FunctionDef f;

      public CurriedScriptFunctionLambda(Class intrface, FunctionDef f, Evaluable[] curriedArgs) {
        super(intrface, curriedArgs);
        this.f = f;
      }

      public Object forwardCall(VarContext ctx, Object[] args) {
        return f.call(ctx, args);
      }
    }

    static public class CurriedConstructorLambda extends CurriedLambdaBase {

      public Constructor[] ctors;

      public CurriedConstructorLambda(Class intrface, Constructor[] ctors, Evaluable[] curriedArgs) {
        super(intrface, curriedArgs);
        this.ctors = ctors;
      }

      public Object forwardCall(VarContext ctx, Object[] args) {
        return preciseNuObject(ctors, args);
      }
    }

    static public class DirectMethodCallOnKnownTarget extends EvaluableBase implements IFieldsToList {

      public boolean widening = false;

      public Object target;

      public Method method;

      public Evaluable[] args;

      public DirectMethodCallOnKnownTarget() {
      }

      public DirectMethodCallOnKnownTarget(boolean widening, Object target, Method method, Evaluable[] args) {
        this.args = args;
        this.method = method;
        this.target = target;
        this.widening = widening;
      }

      public Object[] _fieldsToList() {
        return new Object[] { widening, target, method, args };
      }

      public Object get(VarContext ctx) {
        var evaluatedArgs = evalArgs(args, ctx);
        return widening ? invokeMethodWithWidening(method, target, evaluatedArgs) : invokeMethod(method, target, evaluatedArgs);
      }

      public String toString() {
        return (target == null ? "" : target + ".") + formatFunctionCall(str(method), args);
      }

      public LASValueDescriptor returnType() {
        return LASValueDescriptor.fromClass(method.getReturnType());
      }
    }

    static public class While extends EvaluableBase implements IFieldsToList {

      public Evaluable condition;

      public Evaluable body;

      public While() {
      }

      public While(Evaluable condition, Evaluable body) {
        this.body = body;
        this.condition = condition;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + condition + ", " + body + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { condition, body };
      }

      public Object get(VarContext ctx) {
        while (!ctx.exiting() && (Boolean) condition.get(ctx)) {
          body.get(ctx);
        }
        return null;
      }
    }

    static public class DoWhile extends EvaluableBase implements IFieldsToList {

      public Evaluable condition;

      public Evaluable body;

      public DoWhile() {
      }

      public DoWhile(Evaluable condition, Evaluable body) {
        this.body = body;
        this.condition = condition;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + condition + ", " + body + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { condition, body };
      }

      public Object get(VarContext ctx) {
        do {
          body.get(ctx);
        } while (!ctx.exiting() && (Boolean) condition.get(ctx));
        return null;
      }
    }

    abstract static public class ForEachBase extends EvaluableBase implements IFieldsToList {

      public Evaluable collection;

      public Evaluable body;

      public ForEachBase() {
      }

      public ForEachBase(Evaluable collection, Evaluable body) {
        this.body = body;
        this.collection = collection;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + collection + ", " + body + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { collection, body };
      }

      public Object get(VarContext ctx) {
        var coll = collection.get(ctx);
        Iterator iterator;
        List out;
        try {
          if (coll == null)
            out = new ArrayList();
          else if (coll.getClass().isArray()) {
            if (coll instanceof Object[]) {
              var array = (Object[]) coll;
              out = emptyList(array.length);
              for (var element : array) {
                if (ctx.exiting())
                  return null;
                processElement(ctx, out, element);
              }
            } else if (coll instanceof byte[]) {
              var array = (byte[]) coll;
              out = emptyList(array.length);
              for (var element : array) {
                if (ctx.exiting())
                  return null;
                processElement(ctx, out, element);
              }
            } else if (coll instanceof short[]) {
              var array = (short[]) coll;
              out = emptyList(array.length);
              for (var element : array) {
                if (ctx.exiting())
                  return null;
                processElement(ctx, out, element);
              }
            } else if (coll instanceof double[]) {
              var array = (double[]) coll;
              out = emptyList(array.length);
              for (var element : array) {
                if (ctx.exiting())
                  return null;
                processElement(ctx, out, element);
              }
            } else if (coll instanceof float[]) {
              var array = (float[]) coll;
              out = emptyList(array.length);
              for (var element : array) {
                if (ctx.exiting())
                  return null;
                processElement(ctx, out, element);
              }
            } else if (coll instanceof int[]) {
              var array = (int[]) coll;
              out = emptyList(array.length);
              for (var element : array) {
                if (ctx.exiting())
                  return null;
                processElement(ctx, out, element);
              }
            } else if (coll instanceof long[]) {
              var array = (long[]) coll;
              out = emptyList(array.length);
              for (var element : array) {
                if (ctx.exiting())
                  return null;
                processElement(ctx, out, element);
              }
            } else if (coll instanceof char[]) {
              var array = (char[]) coll;
              out = emptyList(array.length);
              for (var element : array) {
                if (ctx.exiting())
                  return null;
                processElement(ctx, out, element);
              }
            } else if (coll instanceof boolean[]) {
              var array = (boolean[]) coll;
              out = emptyList(array.length);
              for (var element : array) {
                if (ctx.exiting())
                  return null;
                processElement(ctx, out, element);
              }
            } else
              throw fail("todo for each with: " + coll);
          } else if (coll instanceof Iterable) {
            out = emptyList((Iterable) coll);
            iterator = ((Iterable) coll).iterator();
            try {
              while (iterator.hasNext()) {
                if (ctx.exiting())
                  return null;
                var element = iterator.next();
                processElement(ctx, out, element);
              }
            } finally {
              if (iterator instanceof AutoCloseable) {
                try {
                  ((AutoCloseable) iterator).close();
                } catch (Exception __e) {
                  throw rethrow(__e);
                }
              }
            }
          } else
            throw fail("Not iterable: " + className(coll));
        } finally {
          loopDone(ctx);
        }
        return out;
      }

      abstract public void processElement(VarContext ctx, List out, Object o);

      abstract public void loopDone(VarContext ctx);
    }

    static public class ForEach extends ForEachBase {

      public ForEach() {
      }

      public String var;

      final public ForEach setVarType(LASValueDescriptor varType) {
        return varType(varType);
      }

      public ForEach varType(LASValueDescriptor varType) {
        this.varType = varType;
        return this;
      }

      final public LASValueDescriptor getVarType() {
        return varType();
      }

      public LASValueDescriptor varType() {
        return varType;
      }

      public LASValueDescriptor varType;

      public ForEach(Evaluable collection, String var, Evaluable body) {
        this.body = body;
        this.var = var;
        this.collection = collection;
      }

      public void processElement(VarContext ctx, List out, Object o) {
        ctx.set(var, o);
        out.add(body.get(ctx));
      }

      public void loopDone(VarContext ctx) {
        ctx.unset(var);
      }
    }

    static public class ForIterator extends EvaluableBase implements IFieldsToList {

      static final public String _fieldOrder = "iterable var body";

      public Evaluable iterable;

      public String var;

      public Evaluable body;

      public ForIterator() {
      }

      public ForIterator(Evaluable iterable, String var, Evaluable body) {
        this.body = body;
        this.var = var;
        this.iterable = iterable;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + iterable + ", " + var + ", " + body + ")";
      }

      public boolean equals(Object o) {
        if (!(o instanceof ForIterator))
          return false;
        ForIterator __5 = (ForIterator) o;
        return eq(iterable, __5.iterable) && eq(var, __5.var) && eq(body, __5.body);
      }

      public int hashCode() {
        int h = -214906825;
        h = boostHashCombine(h, _hashCode(iterable));
        h = boostHashCombine(h, _hashCode(var));
        h = boostHashCombine(h, _hashCode(body));
        return h;
      }

      public Object[] _fieldsToList() {
        return new Object[] { iterable, var, body };
      }

      public Object get(VarContext ctx) {
        VarContext subContext = new FlexibleVarContext(ctx);
        var iterable = this.iterable.get(ctx);
        Iterator iterator = iterator_gen(iterable);
        return new MapI(value -> {
          subContext.set(var, value);
          return body.get(subContext);
        }, iterator);
      }
    }

    static public class ForNested extends EvaluableBase implements IFieldsToList {

      static final public String _fieldOrder = "iterable var body";

      public Evaluable iterable;

      public String var;

      public Evaluable body;

      public ForNested() {
      }

      public ForNested(Evaluable iterable, String var, Evaluable body) {
        this.body = body;
        this.var = var;
        this.iterable = iterable;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + iterable + ", " + var + ", " + body + ")";
      }

      public boolean equals(Object o) {
        if (!(o instanceof ForNested))
          return false;
        ForNested __6 = (ForNested) o;
        return eq(iterable, __6.iterable) && eq(var, __6.var) && eq(body, __6.body);
      }

      public int hashCode() {
        int h = -1363247360;
        h = boostHashCombine(h, _hashCode(iterable));
        h = boostHashCombine(h, _hashCode(var));
        h = boostHashCombine(h, _hashCode(body));
        return h;
      }

      public Object[] _fieldsToList() {
        return new Object[] { iterable, var, body };
      }

      public Object get(VarContext ctx) {
        VarContext subContext = new FlexibleVarContext(ctx);
        var iterable = this.iterable.get(ctx);
        Iterator iterator = iterator_gen(iterable);
        return nestedIterator(iterator, value -> {
          subContext.set(var, value);
          return iterator_gen(body.get(subContext));
        });
      }
    }

    static public class ForPairs extends ForEachBase {

      public ForPairs() {
      }

      public String varA, varB;

      public ForPairs(Evaluable collection, Evaluable body, String varA, String varB) {
        this.varB = varB;
        this.varA = varA;
        this.body = body;
        this.collection = collection;
      }

      public void processElement(VarContext ctx, List out, Object o) {
        Pair p = (Pair) o;
        ctx.set(varA, p.a);
        ctx.set(varB, p.b);
        out.add(body.get(ctx));
      }

      public void loopDone(VarContext ctx) {
        ctx.unset(varA);
        ctx.unset(varB);
      }
    }

    static public class ForKeyValue extends EvaluableBase implements IFieldsToList {

      public Evaluable map;

      public Evaluable body;

      public String varA;

      public String varB;

      public ForKeyValue() {
      }

      public ForKeyValue(Evaluable map, Evaluable body, String varA, String varB) {
        this.varB = varB;
        this.varA = varA;
        this.body = body;
        this.map = map;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + map + ", " + body + ", " + varA + ", " + varB + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { map, body, varA, varB };
      }

      public Object get(VarContext ctx) {
        Map<?, ?> theMap = (Map) map.get(ctx);
        List out;
        try {
          if (theMap != null) {
            out = emptyList(theMap.size());
            for (var entry : theMap.entrySet()) {
              if (ctx.exiting())
                return null;
              ctx.set(varA, entry.getKey());
              ctx.set(varB, entry.getValue());
              out.add(body.get(ctx));
            }
          } else
            out = new ArrayList();
        } finally {
          ctx.unset(varA);
          ctx.unset(varB);
        }
        return out;
      }
    }

    static public class ForIntTo extends EvaluableBase implements IFieldsToList {

      public Evaluable endValue;

      public String var;

      public Evaluable body;

      public ForIntTo() {
      }

      public ForIntTo(Evaluable endValue, String var, Evaluable body) {
        this.body = body;
        this.var = var;
        this.endValue = endValue;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + endValue + ", " + var + ", " + body + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { endValue, var, body };
      }

      public Evaluable optimize() {
        if (!returnValueNeeded)
          body = body.optimizeForReturnValueNotNeeded();
        return this;
      }

      public Object get(VarContext ctx) {
        int n = (Integer) endValue.get(ctx), i = 0;
        List out = returnValueNeeded ? new ArrayList() : null;
        try {
          ctx.put(var, i);
          while (i < n) {
            if (ctx.exiting())
              return null;
            Object o = body.get(ctx);
            {
              if (out != null)
                out.add(o);
            }
            ctx.set(var, i = (Integer) ctx.get(var) + 1);
          }
        } finally {
          ctx.unset(var);
        }
        return out;
      }
    }

    static public class ForIndex extends EvaluableBase {

      public ForIndex() {
      }

      public Evaluable collection, body;

      public String varIndex, varElement;

      public ForIndex(Evaluable collection, Evaluable body, String varIndex, String varElement) {
        this.varElement = varElement;
        this.varIndex = varIndex;
        this.body = body;
        this.collection = collection;
      }

      public Object get(VarContext ctx) {
        return new ForIndex_instance(collection, body, varIndex, varElement).get(ctx);
      }
    }

    static public class ForIndex_instance extends ForEachBase {

      public String varIndex, varElement;

      public int index;

      public ForIndex_instance(Evaluable collection, Evaluable body, String varIndex, String varElement) {
        this.varElement = varElement;
        this.varIndex = varIndex;
        this.body = body;
        this.collection = collection;
      }

      public void processElement(VarContext ctx, List out, Object o) {
        ctx.set(varIndex, index++);
        ctx.set(varElement, o);
        out.add(body.get(ctx));
      }

      public void loopDone(VarContext ctx) {
        ctx.unset(varIndex);
        ctx.unset(varElement);
      }
    }

    static public class IfThen extends EvaluableBase implements IFieldsToList {

      public Evaluable condition;

      public Evaluable body;

      public Evaluable elseBranch;

      public IfThen() {
      }

      public IfThen(Evaluable condition, Evaluable body, Evaluable elseBranch) {
        this.elseBranch = elseBranch;
        this.body = body;
        this.condition = condition;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + condition + ", " + body + ", " + elseBranch + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { condition, body, elseBranch };
      }

      public IfThen(Evaluable condition, Evaluable body) {
        this.condition = condition;
        this.body = body;
      }

      public Object get(VarContext ctx) {
        if ((Boolean) condition.get(ctx))
          return body.get(ctx);
        else if (elseBranch != null)
          return elseBranch.get(ctx);
        else
          return null;
      }
    }

    static public class ReturnFromScript extends EvaluableBase implements IFieldsToList {

      public Script script;

      public Evaluable value;

      public ReturnFromScript() {
      }

      public ReturnFromScript(Script script, Evaluable value) {
        this.value = value;
        this.script = script;
      }

      public Object[] _fieldsToList() {
        return new Object[] { script, value };
      }

      public Object get(VarContext ctx) {
        Object result = value.get(ctx);
        ctx.exitFromScript(script);
        ctx.returnValue(result);
        return null;
      }

      public String toString() {
        return formatFunctionCall("ReturnFromScript", script, value);
      }
    }

    static public class Continue extends EvaluableBase implements IFieldsToList {

      public Script loopBody;

      public Continue() {
      }

      public Continue(Script loopBody) {
        this.loopBody = loopBody;
      }

      public Object[] _fieldsToList() {
        return new Object[] { loopBody };
      }

      public Object get(VarContext ctx) {
        ctx.exitFromScript(loopBody);
        ctx.returnValue(null);
        return null;
      }

      public String toString() {
        return formatFunctionCall("Continue", loopBody);
      }
    }

    static public class RepeatN extends EvaluableBase implements IFieldsToList {

      public Evaluable n;

      public Evaluable body;

      public RepeatN() {
      }

      public RepeatN(Evaluable n, Evaluable body) {
        this.body = body;
        this.n = n;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + n + ", " + body + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { n, body };
      }

      public Object get(VarContext ctx) {
        long count = ((Number) n.get(ctx)).longValue();
        for (int _repeat_0 = 0; _repeat_0 < count; _repeat_0++) {
          if (ctx.exiting())
            return null;
          body.get(ctx);
        }
        return null;
      }
    }

    static public class BoolAnd extends EvaluableBase implements IFieldsToList {

      public Evaluable a;

      public Evaluable b;

      public BoolAnd() {
      }

      public BoolAnd(Evaluable a, Evaluable b) {
        this.b = b;
        this.a = a;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + a + ", " + b + ")";
      }

      public boolean equals(Object o) {
        if (!(o instanceof BoolAnd))
          return false;
        BoolAnd __7 = (BoolAnd) o;
        return eq(a, __7.a) && eq(b, __7.b);
      }

      public int hashCode() {
        int h = 1729330797;
        h = boostHashCombine(h, _hashCode(a));
        h = boostHashCombine(h, _hashCode(b));
        return h;
      }

      public Object[] _fieldsToList() {
        return new Object[] { a, b };
      }

      public Object get(VarContext ctx) {
        if (!((Boolean) a.get(ctx)))
          return false;
        return b.get(ctx);
      }
    }

    static public class BoolOr extends EvaluableBase implements IFieldsToList {

      public Evaluable a;

      public Evaluable b;

      public BoolOr() {
      }

      public BoolOr(Evaluable a, Evaluable b) {
        this.b = b;
        this.a = a;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + a + ", " + b + ")";
      }

      public boolean equals(Object o) {
        if (!(o instanceof BoolOr))
          return false;
        BoolOr __8 = (BoolOr) o;
        return eq(a, __8.a) && eq(b, __8.b);
      }

      public int hashCode() {
        int h = 1995447949;
        h = boostHashCombine(h, _hashCode(a));
        h = boostHashCombine(h, _hashCode(b));
        return h;
      }

      public Object[] _fieldsToList() {
        return new Object[] { a, b };
      }

      public Object get(VarContext ctx) {
        if (((Boolean) a.get(ctx)))
          return true;
        return b.get(ctx);
      }
    }

    static public class TempBlock extends EvaluableBase implements IFieldsToList {

      public Evaluable tempExpr;

      public Evaluable body;

      public TempBlock() {
      }

      public TempBlock(Evaluable tempExpr, Evaluable body) {
        this.body = body;
        this.tempExpr = tempExpr;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + tempExpr + ", " + body + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { tempExpr, body };
      }

      public Object get(VarContext ctx) {
        AutoCloseable __2 = (AutoCloseable) (tempExpr.get(ctx));
        try {
          return body.get(ctx);
        } finally {
          _close(__2);
        }
      }
    }

    static public class WillReturn extends EvaluableBase implements IFieldsToList {

      public Evaluable exp;

      public Evaluable body;

      public WillReturn() {
      }

      public WillReturn(Evaluable exp, Evaluable body) {
        this.body = body;
        this.exp = exp;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + exp + ", " + body + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { exp, body };
      }

      public Object get(VarContext ctx) {
        body.get(ctx);
        return exp.get(ctx);
      }
    }

    static public class ClassDef extends EvaluableBase implements IFieldsToList {

      public ResolvableLASClass lasClass;

      public ClassDef() {
      }

      public ClassDef(ResolvableLASClass lasClass) {
        this.lasClass = lasClass;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + lasClass + ")";
      }

      public boolean equals(Object o) {
        if (!(o instanceof ClassDef))
          return false;
        ClassDef __9 = (ClassDef) o;
        return eq(lasClass, __9.lasClass);
      }

      public int hashCode() {
        int h = 757052301;
        h = boostHashCombine(h, _hashCode(lasClass));
        return h;
      }

      public Object[] _fieldsToList() {
        return new Object[] { lasClass };
      }

      public Object get(VarContext ctx) {
        return lasClass.get();
      }
    }

    static public class SetField extends EvaluableBase implements IFieldsToList {

      public Evaluable target;

      public String name;

      public Evaluable expr;

      public SetField() {
      }

      public SetField(Evaluable target, String name, Evaluable expr) {
        this.expr = expr;
        this.name = name;
        this.target = target;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + target + ", " + name + ", " + expr + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { target, name, expr };
      }

      final public SetField setAllowNullReference(boolean allowNullReference) {
        return allowNullReference(allowNullReference);
      }

      public SetField allowNullReference(boolean allowNullReference) {
        this.allowNullReference = allowNullReference;
        return this;
      }

      final public boolean getAllowNullReference() {
        return allowNullReference();
      }

      public boolean allowNullReference() {
        return allowNullReference;
      }

      public boolean allowNullReference = false;

      public Object handleNullReference() {
        if (allowNullReference)
          return null;
        else
          throw new NullPointerException();
      }

      public Object get(VarContext ctx) {
        try {
          Object value = expr.get(ctx);
          Object object = target.get(ctx);
          if (object == null)
            handleNullReference();
          else
            set(object, name, value);
          return value;
        } catch (Throwable e) {
          throw rethrowWithSrc(e);
        }
      }
    }

    static public class SetStaticField extends EvaluableBase implements IFieldsToList {

      public Field field;

      public Evaluable expr;

      public SetStaticField() {
      }

      public SetStaticField(Field field, Evaluable expr) {
        this.expr = expr;
        this.field = field;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + field + ", " + expr + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { field, expr };
      }

      public Object get(VarContext ctx) {
        try {
          Object value = expr.get(ctx);
          field.set(null, value);
          return value;
        } catch (Throwable e) {
          throw rethrowWithSrc(e);
        }
      }
    }

    static public class Throw extends EvaluableBase implements IFieldsToList {

      public Evaluable expr;

      public Throw() {
      }

      public Throw(Evaluable expr) {
        this.expr = expr;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + expr + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { expr };
      }

      public Object get(VarContext ctx) {
        throw asRuntimeException((Throwable) expr.get(ctx));
      }
    }

    static public class TryCatch extends EvaluableBase implements IFieldsToList {

      public Evaluable body;

      public String var;

      public Evaluable catchBlock;

      public TryCatch() {
      }

      public TryCatch(Evaluable body, String var, Evaluable catchBlock) {
        this.catchBlock = catchBlock;
        this.var = var;
        this.body = body;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + body + ", " + var + ", " + catchBlock + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { body, var, catchBlock };
      }

      public Object get(VarContext ctx) {
        try {
          return body.get(ctx);
        } catch (Throwable e) {
          var addVar = var == null ? null : ctx.tempPut(var, e);
          AutoCloseable __3 = addVar;
          try {
            return catchBlock.get(ctx);
          } finally {
            _close(__3);
          }
        }
      }
    }

    static public class TryFinally extends EvaluableBase implements IFieldsToList {

      public Evaluable body;

      public Evaluable finallyBlock;

      public TryFinally() {
      }

      public TryFinally(Evaluable body, Evaluable finallyBlock) {
        this.finallyBlock = finallyBlock;
        this.body = body;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + body + ", " + finallyBlock + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { body, finallyBlock };
      }

      public Object get(VarContext ctx) {
        try {
          return body.get(ctx);
        } finally {
          finallyBlock.get(ctx);
        }
      }
    }

    static public structure_Data structureDataForLAS() {
      structure_Data d = new structure_Data();
      d.skipDefaultValues(true);
      d.shouldIncludeField = field -> {
        String c = shortClassName(field.getDeclaringClass());
        String f = field.getName();
        boolean shouldInclude = !(eq(c, "HasTokenRangeWithSrc") && eq(f, "src"));
        return shouldInclude;
      };
      return d;
    }

    static public boolean isUnproblematicValue(Object o) {
      return o == null || o instanceof Number || o instanceof String || o instanceof Boolean || o instanceof Class;
    }

    static public String scriptStruct(Object o) {
      String s = struct(o, structureDataForLAS());
      List<String> tok = structTok(s);
      String prefix = shortName(GazelleV_LeftArrowScript.class) + "$";
      for (int i = 1; i < l(tok); i += 2) tok.set(i, replacePrefix(prefix, "$", tok.get(i)));
      return join(tok);
    }

    static public String indentedScriptStruct(Object o) {
      return indentStructureString(scriptStruct(o));
    }

    static public Evaluable _const(Object o) {
      return new Const(o);
    }

    static public class Then extends CallOnTarget {

      public CallOnTarget call1, call2;

      public Then(CallOnTarget call1, CallOnTarget call2) {
        this.call2 = call2;
        this.call1 = call1;
        target = call1.target;
        call1.target = null;
      }

      public Object evalOnTarget(VarContext ctx, Object object) {
        call1.evalOnTarget(ctx, object);
        return call2.evalOnTarget(ctx, object);
      }
    }

    static public class Synchronized extends EvaluableBase implements IFieldsToList {

      public Evaluable target;

      public Evaluable body;

      public Synchronized() {
      }

      public Synchronized(Evaluable target, Evaluable body) {
        this.body = body;
        this.target = target;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + target + ", " + body + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { target, body };
      }

      public Evaluable optimize() {
        if (!returnValueNeeded)
          body = body.optimizeForReturnValueNotNeeded();
        return this;
      }

      public Object get(VarContext ctx) {
        synchronized (target.get(ctx)) {
          return body.get(ctx);
        }
      }
    }
  }

  static public interface IVF2<A, B> {

    public void get(A a, B b);
  }

  static public interface WidthAndHeight {

    default public int w() {
      return getWidth();
    }

    default public int width() {
      return getWidth();
    }

    public int getWidth();

    default public int h() {
      return getHeight();
    }

    default public int height() {
      return getHeight();
    }

    public int getHeight();

    public default Rect bounds() {
      return rect(0, 0, getWidth(), getHeight());
    }

    default public int area() {
      return toInt(areaAsLong());
    }

    default public long areaAsLong() {
      return longMul(w(), h());
    }
  }

  static public class SynchronizedList<E> extends SynchronizedCollection<E> implements List<E> {

    public SynchronizedList() {
    }

    public List<E> list;

    public SynchronizedList(List<E> list) {
      super(list);
      this.list = list;
    }

    public SynchronizedList(List<E> list, Object mutex) {
      super(list, mutex);
      this.list = list;
    }

    public boolean equals(Object o) {
      if (this == o)
        return true;
      synchronized (mutex) {
        return list.equals(o);
      }
    }

    public int hashCode() {
      synchronized (mutex) {
        return list.hashCode();
      }
    }

    public E get(int index) {
      synchronized (mutex) {
        return list.get(index);
      }
    }

    public E set(int index, E element) {
      synchronized (mutex) {
        return list.set(index, element);
      }
    }

    public void add(int index, E element) {
      synchronized (mutex) {
        list.add(index, element);
      }
    }

    public E remove(int index) {
      synchronized (mutex) {
        return list.remove(index);
      }
    }

    public int indexOf(Object o) {
      synchronized (mutex) {
        return list.indexOf(o);
      }
    }

    public int lastIndexOf(Object o) {
      synchronized (mutex) {
        return list.lastIndexOf(o);
      }
    }

    public boolean addAll(int index, Collection<? extends E> c) {
      synchronized (mutex) {
        return list.addAll(index, c);
      }
    }

    public ListIterator<E> listIterator() {
      return list.listIterator();
    }

    public ListIterator<E> listIterator(int index) {
      return list.listIterator(index);
    }

    public List<E> subList(int fromIndex, int toIndex) {
      synchronized (mutex) {
        return new SynchronizedList<>(list.subList(fromIndex, toIndex), mutex);
      }
    }

    @Override
    public void replaceAll(java.util.function.UnaryOperator<E> operator) {
      synchronized (mutex) {
        list.replaceAll(operator);
      }
    }

    @Override
    public void sort(Comparator<? super E> c) {
      synchronized (mutex) {
        list.sort(c);
      }
    }

    @java.io.Serial
    private Object readResolve() {
      return (list instanceof RandomAccess ? new SynchronizedRandomAccessList<>(list) : this);
    }
  }

  static public class LASValueDescriptor {

    public LASValueDescriptor() {
    }

    public boolean knownValue() {
      return false;
    }

    public Object value() {
      return null;
    }

    public Type type() {
      return javaClass();
    }

    public Class javaClass() {
      return null;
    }

    public boolean javaClassIsExact() {
      return false;
    }

    public boolean canBeNull() {
      return true;
    }

    public boolean canFail() {
      return false;
    }

    public boolean willFail() {
      return false;
    }

    static public class Exact extends LASValueDescriptor implements IFieldsToList {

      public Class c;

      public boolean canBeNull = false;

      public Exact() {
      }

      public Exact(Class c, boolean canBeNull) {
        this.canBeNull = canBeNull;
        this.c = c;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + c + ", " + canBeNull + ")";
      }

      public boolean equals(Object o) {
        if (!(o instanceof Exact))
          return false;
        Exact __1 = (Exact) o;
        return eq(c, __1.c) && eq(canBeNull, __1.canBeNull);
      }

      public int hashCode() {
        int h = 67394271;
        h = boostHashCombine(h, _hashCode(c));
        h = boostHashCombine(h, _hashCode(canBeNull));
        return h;
      }

      public Object[] _fieldsToList() {
        return new Object[] { c, canBeNull };
      }

      public Class javaClass() {
        return c;
      }

      public boolean javaClassIsExact() {
        return true;
      }

      public boolean canBeNull() {
        return canBeNull;
      }
    }

    static public class NonExact extends LASValueDescriptor implements IFieldsToList {

      public Class c;

      public boolean canBeNull = false;

      public NonExact() {
      }

      public NonExact(Class c, boolean canBeNull) {
        this.canBeNull = canBeNull;
        this.c = c;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + c + ", " + canBeNull + ")";
      }

      public boolean equals(Object o) {
        if (!(o instanceof NonExact))
          return false;
        NonExact __2 = (NonExact) o;
        return eq(c, __2.c) && eq(canBeNull, __2.canBeNull);
      }

      public int hashCode() {
        int h = 1445514322;
        h = boostHashCombine(h, _hashCode(c));
        h = boostHashCombine(h, _hashCode(canBeNull));
        return h;
      }

      public Object[] _fieldsToList() {
        return new Object[] { c, canBeNull };
      }

      public Class javaClass() {
        return c;
      }

      public boolean javaClassIsExact() {
        return false;
      }

      public boolean canBeNull() {
        return canBeNull;
      }
    }

    static public class NonExactType extends LASValueDescriptor implements IFieldsToList {

      static final public String _fieldOrder = "type canBeNull";

      public Type type;

      public boolean canBeNull = false;

      public NonExactType() {
      }

      public NonExactType(Type type, boolean canBeNull) {
        this.canBeNull = canBeNull;
        this.type = type;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + type + ", " + canBeNull + ")";
      }

      public boolean equals(Object o) {
        if (!(o instanceof NonExactType))
          return false;
        NonExactType __3 = (NonExactType) o;
        return eq(type, __3.type) && eq(canBeNull, __3.canBeNull);
      }

      public int hashCode() {
        int h = 1099847340;
        h = boostHashCombine(h, _hashCode(type));
        h = boostHashCombine(h, _hashCode(canBeNull));
        return h;
      }

      public Object[] _fieldsToList() {
        return new Object[] { type, canBeNull };
      }

      public Type type() {
        return type;
      }

      public boolean javaClassIsExact() {
        return false;
      }

      public boolean canBeNull() {
        return canBeNull;
      }
    }

    static public LASValueDescriptor nonExactCanBeNull(Type c) {
      return new NonExact(typeToClass(c), true);
    }

    static public class KnownValue extends LASValueDescriptor implements IFieldsToList {

      public Object value;

      public KnownValue() {
      }

      public KnownValue(Object value) {
        this.value = value;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + value + ")";
      }

      public boolean equals(Object o) {
        if (!(o instanceof KnownValue))
          return false;
        KnownValue __4 = (KnownValue) o;
        return eq(value, __4.value);
      }

      public int hashCode() {
        int h = -1456305138;
        h = boostHashCombine(h, _hashCode(value));
        return h;
      }

      public Object[] _fieldsToList() {
        return new Object[] { value };
      }

      public boolean knownValue() {
        return true;
      }

      public Object value() {
        return value;
      }

      public Class javaClass() {
        return value == null ? null : value.getClass();
      }

      public boolean javaClassIsExact() {
        return value != null;
      }

      public boolean canBeNull() {
        return value == null;
      }
    }

    static public class WillFail extends LASValueDescriptor {

      public boolean canFail() {
        return true;
      }

      public boolean willFail() {
        return true;
      }
    }

    static public LASValueDescriptor fromClass(Class c) {
      return new NonExact(c, true);
    }

    static public LASValueDescriptor fromType(Type type) {
      return new NonExact(typeToClass(type), true);
    }

    public String toString() {
      return "LASValueDescriptor " + javaClass();
    }
  }

  static public class FlexibleVarContext extends VarContext {

    public Map<String, Object> vars;

    public FlexibleVarContext() {
    }

    public FlexibleVarContext(VarContext parent) {
      super(parent);
    }

    public Object get(String name) {
      if (containsKey(vars, name))
        return mapGet(vars, name);
      if (parent != null)
        return parent.get(name);
      return null;
    }

    public void set(String name, Object value) {
      vars = putOrCreateSyncMap(vars, name, value);
    }

    public AutoCloseable tempSet(String name, Object value) {
      initMap();
      return main.tempPut(vars, name, value);
    }

    public void unset(String name) {
      remove(vars, name);
    }

    public Map<String, Object> varMap() {
      return vars;
    }

    final public void initMap() {
      makeThreadSafe();
    }

    public void makeThreadSafe() {
      if (vars == null)
        vars = syncHashMap();
    }
  }

  static public class PingSourceCancelledException extends RuntimeException implements IFieldsToList {

    public PingSource pingSource;

    public PingSourceCancelledException() {
    }

    public PingSourceCancelledException(PingSource pingSource) {
      this.pingSource = pingSource;
    }

    public String toString() {
      return shortClassName_dropNumberPrefix(this) + "(" + pingSource + ")";
    }

    public Object[] _fieldsToList() {
      return new Object[] { pingSource };
    }
  }

  static public class TokenRangeWithSrc extends TokenRange {

    public List<String> tok;

    final public TokenRangeWithSrc setSourceInfo(Object sourceInfo) {
      return sourceInfo(sourceInfo);
    }

    public TokenRangeWithSrc sourceInfo(Object sourceInfo) {
      this.sourceInfo = sourceInfo;
      return this;
    }

    final public Object getSourceInfo() {
      return sourceInfo();
    }

    public Object sourceInfo() {
      return sourceInfo;
    }

    public Object sourceInfo;

    public TokenRangeWithSrc() {
    }

    public TokenRangeWithSrc(List<String> tok, int start) {
      this.start = start;
      end = start;
    }

    public TokenRangeWithSrc(List<String> tok, int start, int end) {
      this.end = end;
      this.start = start;
    }

    public TokenRangeWithSrc(ListAndIndex<String> startPtr, ListAndIndex<String> endPtr) {
      assertNotNull("startPtr", startPtr);
      assertNotNull("endPtr", endPtr);
      assertSame(tok = startPtr.list(), endPtr.list());
      start = startPtr.idx();
      end = endPtr.idx();
    }

    public ListAndIndex<String> startPtr() {
      return new ListAndIndex(tok, start);
    }

    public ListAndIndex<String> endPtr() {
      return new ListAndIndex(tok, end);
    }

    public LineAndColumn startLineAndCol() {
      return tokenToLineAndColumn(startPtr());
    }

    public LineAndColumn endLineAndCol() {
      return tokenToLineAndColumn(endPtr());
    }

    public String text() {
      return joinSubList(tok, start, end);
    }

    public String renderRange() {
      return renderLineAndColumnRange(startLineAndCol(), endLineAndCol());
    }

    public String toString() {
      return spaceCombine(sourceInfo(), renderRange());
    }

    public String fullSourceText() {
      return join(tok);
    }
  }

  static public interface IMeta {

    public void _setMeta(Object meta);

    public Object _getMeta();

    default public IAutoCloseableF0 _tempMetaMutex() {
      return new IAutoCloseableF0() {

        public Object get() {
          return IMeta.this;
        }

        public void close() {
        }
      };
    }

    default public Object getMeta(Object obj, Object key) {
      return metaGet(obj, key);
    }

    default public Object metaGet(Object obj, Object key) {
      return metaMapGet(obj, key);
    }

    default public Object metaGet(String key, Object obj) {
      return metaMapGet(obj, key);
    }

    default public Object getMeta(Object key) {
      return metaGet(key);
    }

    default public Object metaGet(Object key) {
      if (key == null)
        return null;
      Object meta = _getMeta();
      if (meta instanceof Map)
        return ((Map) meta).get(key);
      return null;
    }

    default public void metaSet(IMeta obj, Object key, Object value) {
      metaPut(obj, key, value);
    }

    default public void metaPut(IMeta obj, Object key, Object value) {
      metaMapPut(obj, key, value);
    }

    default public void metaSet(Object key, Object value) {
      metaPut(key, value);
    }

    default public void metaPut(Object key, Object value) {
      if (key == null)
        return;
      Map map = convertObjectMetaToMap(this);
      syncMapPutOrRemove(map, key, value);
    }
  }

  static public class ClassNameResolver {

    final public ClassNameResolver setByteCodePath(File byteCodePath) {
      return byteCodePath(byteCodePath);
    }

    public ClassNameResolver byteCodePath(File byteCodePath) {
      this.byteCodePath = byteCodePath;
      return this;
    }

    final public File getByteCodePath() {
      return byteCodePath();
    }

    public File byteCodePath() {
      return byteCodePath;
    }

    public File byteCodePath = byteCodePathForClass(getClass());

    public List<String> importedPackages = itemPlusList("java.lang", endingWith_dropSuffix(standardImports(), ".*"));

    public Set<String> allFullyQualifiedClassNames_cache;

    public Set<String> allFullyQualifiedClassNames() {
      if (allFullyQualifiedClassNames_cache == null)
        allFullyQualifiedClassNames_cache = allFullyQualifiedClassNames_load();
      return allFullyQualifiedClassNames_cache;
    }

    public Set<String> allFullyQualifiedClassNames_load() {
      Set<String> set = new HashSet();
      assertNotNull(byteCodePath);
      set.addAll(classNamesInJarOrDir(byteCodePath));
      printVars("ClassNameResolver", "byteCodePath", byteCodePath, "classesFound", l(set));
      set.addAll(classNamesInLoadedJigsawModules());
      return set;
    }

    public ClassNameResolver init() {
      allFullyQualifiedClassNames();
      return this;
    }

    public String findClass(String name) {
      for (String pkg : importedPackages) {
        String fullName = pkg + "." + name;
        if (allFullyQualifiedClassNames().contains(fullName))
          return fullName;
      }
      return null;
    }

    public void printMe() {
      printVars("ClassNameResolver", "byteCodePath", byteCodePath);
      print("importedPackages", importedPackages);
    }
  }

  static public class ThreadPool implements AutoCloseable {

    public int max = numberOfCores();

    public List<PooledThread> all = new ArrayList();

    public Set<PooledThread> used = new HashSet();

    public Set<PooledThread> free = new HashSet();

    public boolean verbose, retired;

    public class InternalPingSource extends PingSource {
    }

    public InternalPingSource internalPingSource = new InternalPingSource();

    public MultiSleeper sleeper = new MultiSleeper();

    public ThreadPool() {
    }

    public ThreadPool(int max) {
      this.max = max;
    }

    synchronized public int maxSize() {
      return max;
    }

    synchronized public int total() {
      return l(used) + l(free);
    }

    transient public Set<Runnable> onCustomerMustWaitAlert;

    public ThreadPool onCustomerMustWaitAlert(Runnable r) {
      onCustomerMustWaitAlert = createOrAddToSyncLinkedHashSet(onCustomerMustWaitAlert, r);
      return this;
    }

    public ThreadPool removeCustomerMustWaitAlertListener(Runnable r) {
      main.remove(onCustomerMustWaitAlert, r);
      return this;
    }

    public void customerMustWaitAlert() {
      if (onCustomerMustWaitAlert != null)
        for (var listener : onCustomerMustWaitAlert) pcallF_typed(listener);
    }

    public void fireCustomerMustWaitAlert() {
      vmBus_send("customerMustWaitAlert", this, currentThread());
      customerMustWaitAlert();
    }

    public PooledThread acquireThreadOrQueue(Runnable action) {
      if (action == null)
        return null;
      PooledThread t;
      synchronized (this) {
        if (_hasFreeAfterCreating()) {
          t = _firstFreeThread();
          markUsed(t);
        } else
          t = _anyThread();
      }
      t.addWork(action);
      return t;
    }

    public boolean _hasFreeAfterCreating() {
      checkNotRetired();
      if (nempty(free))
        return true;
      if (total() < max) {
        PooledThread t = newThread();
        all.add(t);
        free.add(t);
        return true;
      }
      return false;
    }

    public PooledThread acquireThreadOrWait(Runnable action) {
      try {
        if (action == null)
          return null;
        PooledThread t;
        while (true) {
          synchronized (this) {
            if (_hasFreeAfterCreating()) {
              t = _firstFreeThread();
              break;
            } else
              _waitWaitWait();
          }
        }
        t.addWork(action);
        return t;
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public PooledThread _firstFreeThread() {
      return first(free);
    }

    public PooledThread _anyThread() {
      return random(used);
    }

    public class PooledThread extends Thread {

      public PooledThread(String name) {
        super(name);
      }

      public AppendableChain<Runnable> q;

      synchronized public Runnable _grabWorkOrSleep() {
        try {
          Runnable r = first(q);
          if (r == null) {
            markFree(this);
            if (verbose)
              print("Thread sleeps");
            synchronized (this) {
              wait();
            }
            if (verbose)
              print("Thread woke up");
            return null;
          }
          q = popFirst(q);
          return r;
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public void run() {
        try {
          pingSource_tl().set(internalPingSource);
          while (!retired()) {
            ping();
            Runnable r = _grabWorkOrSleep();
            if (verbose)
              print(this + " work: " + r);
            if (r != null)
              try {
                if (verbose)
                  print(this + " running: " + r);
                r.run();
                pingSource_tl().set(internalPingSource);
                if (verbose)
                  print(this + " done");
              } catch (Throwable e) {
                pingSource_tl().set(internalPingSource);
                if (verbose)
                  print(this + " error");
                printStackTrace(e);
              } finally {
                pingSource_tl().set(internalPingSource);
                if (verbose)
                  print("ThreadPool finally");
              }
          }
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      synchronized public boolean isEmpty() {
        return empty(q);
      }

      public void addWork(Runnable r) {
        if (verbose)
          print("Added work to " + this + ": " + r);
        synchronized (this) {
          q = chainPlus(q, r);
          notifyAll();
        }
      }
    }

    public PooledThread newThread() {
      PooledThread t = new PooledThread("Thread Pool Inhabitant " + n2(total() + 1));
      t.start();
      return t;
    }

    synchronized public void markFree(PooledThread t) {
      used.remove(t);
      free.add(t);
      notifyAll();
    }

    synchronized public void markUsed(PooledThread t) {
      free.remove(t);
      used.add(t);
    }

    synchronized public String toString() {
      return retired() ? "Retired ThreadPool" : "ThreadPool " + roundBracket(commaCombine(n2(used) + " used out of " + n2(total()), max <= total() ? null : "could grow to " + n2(max)));
    }

    synchronized public boolean retired() {
      return retired;
    }

    synchronized public void retire() {
      if (verbose)
        print("ThreadPool Retiring");
      retired = true;
      for (var thread : free) syncNotifyAll(thread);
    }

    public void checkNotRetired() {
      if (retired())
        throw fail("retired");
    }

    synchronized public void close() {
      try {
        retire();
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public void _waitWaitWait() {
      try {
        do {
          fireCustomerMustWaitAlert();
          wait();
          checkNotRetired();
        } while (empty(free));
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public void dO(String text, Runnable r) {
      if (r == null)
        return;
      new PingSource(this, text).dO(r);
    }

    public ISleeper_v2 sleeper() {
      return sleeper;
    }
  }

  static final public class ParameterizedTypeImpl implements ParameterizedType {

    public ParameterizedTypeImpl() {
    }

    public Type ownerType;

    public Type rawType;

    public Type[] typeArguments;

    public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) {
      this.typeArguments = typeArguments;
      this.rawType = rawType;
      this.ownerType = ownerType;
    }

    public Type[] getActualTypeArguments() {
      return typeArguments;
    }

    public Type getRawType() {
      return rawType;
    }

    public Type getOwnerType() {
      return ownerType;
    }

    @Override
    public boolean equals(Object other) {
      if (other instanceof ParameterizedType)
        return eq(ownerType, ((ParameterizedType) other).getOwnerType()) && eq(rawType, ((ParameterizedType) other).getRawType()) && eq(asList(typeArguments), asList(((ParameterizedType) other).getActualTypeArguments()));
      return false;
    }

    @Override
    public int hashCode() {
      return Arrays.hashCode(typeArguments) ^ rawType.hashCode() ^ _hashCode(ownerType);
    }

    @Override
    public String toString() {
      int length = typeArguments.length;
      if (length == 0)
        return typeToString(rawType);
      StringBuilder stringBuilder = new StringBuilder();
      stringBuilder.append(typeToString(rawType)).append("<").append(typeToString(typeArguments[0]));
      for (int i = 1; i < length; i++) {
        stringBuilder.append(", ").append(typeToString(typeArguments[i]));
      }
      return stringBuilder.append(">").toString();
    }

    static public String typeToString(Type type) {
      return type instanceof Class ? ((Class<?>) type).getName() : type.toString();
    }
  }

  static public class GenericArrayTypeImpl implements GenericArrayType {

    final public Type genericComponentType;

    public GenericArrayTypeImpl(Type ct) {
      genericComponentType = ct;
    }

    public static GenericArrayTypeImpl make(Type ct) {
      return new GenericArrayTypeImpl(ct);
    }

    public Type getGenericComponentType() {
      return genericComponentType;
    }

    public String toString() {
      return getGenericComponentType().getTypeName() + "[]";
    }

    @Override
    public boolean equals(Object o) {
      if (o instanceof GenericArrayType) {
        GenericArrayType that = (GenericArrayType) o;
        return Objects.equals(genericComponentType, that.getGenericComponentType());
      } else
        return false;
    }

    @Override
    public int hashCode() {
      return Objects.hashCode(genericComponentType);
    }
  }

  public interface ILASClassLoader {

    public Class<?> defineLASClass(String name, IF0<byte[]> generateClass);

    public Object rememberClassBytes(boolean rememberClassBytes);
  }

  static public class LASClassDef extends HasTokenRangeWithSrc {

    final public LASClassDef setUserGivenName(String userGivenName) {
      return userGivenName(userGivenName);
    }

    public LASClassDef userGivenName(String userGivenName) {
      this.userGivenName = userGivenName;
      return this;
    }

    final public String getUserGivenName() {
      return userGivenName();
    }

    public String userGivenName() {
      return userGivenName;
    }

    public String userGivenName;

    final public LASClassDef setClassDefPrefix(String classDefPrefix) {
      return classDefPrefix(classDefPrefix);
    }

    public LASClassDef classDefPrefix(String classDefPrefix) {
      this.classDefPrefix = classDefPrefix;
      return this;
    }

    final public String getClassDefPrefix() {
      return classDefPrefix();
    }

    public String classDefPrefix() {
      return classDefPrefix;
    }

    public String classDefPrefix = "userCode.";

    final public LASClassDef setFullCompilation(boolean fullCompilation) {
      return fullCompilation(fullCompilation);
    }

    public LASClassDef fullCompilation(boolean fullCompilation) {
      this.fullCompilation = fullCompilation;
      return this;
    }

    final public boolean getFullCompilation() {
      return fullCompilation();
    }

    public boolean fullCompilation() {
      return fullCompilation;
    }

    public boolean fullCompilation = false;

    final public LASClassDef setVerbose(boolean verbose) {
      return verbose(verbose);
    }

    public LASClassDef verbose(boolean verbose) {
      this.verbose = verbose;
      return this;
    }

    final public boolean getVerbose() {
      return verbose();
    }

    public boolean verbose() {
      return verbose;
    }

    public boolean verbose = true;

    transient public Class resolvedClass;

    final public LASClassDef setSuperClass(Type superClass) {
      return superClass(superClass);
    }

    public LASClassDef superClass(Type superClass) {
      this.superClass = superClass;
      return this;
    }

    final public Type getSuperClass() {
      return superClass();
    }

    public Type superClass() {
      return superClass;
    }

    public Type superClass = Object.class;

    public List<FieldDef> fields = new ArrayList();

    public Map<String, FieldDef> fieldsByName = new HashMap();

    public List<GazelleV_LeftArrowScript.FunctionDef> methods = new ArrayList();

    public MultiMap<String, GazelleV_LeftArrowScript.FunctionDef> methodsByName = new MultiMap();

    public List<GazelleV_LeftArrowScript.FunctionDef> methodBodies = new ArrayList();

    public List<Type> interfaces = new ArrayList();

    public GazelleV_LeftArrowScript.FunctionDef initializerMethod;

    public List<GazelleV_LeftArrowScript.Evaluable> initializers = new ArrayList();

    public BitSet isFieldInitializer = new BitSet();

    public List<Pair<String, GazelleV_LeftArrowScript.Evaluable>> staticFieldInitializers = new ArrayList();

    static public class FieldDef {

      final public FieldDef setName(String name) {
        return name(name);
      }

      public FieldDef name(String name) {
        this.name = name;
        return this;
      }

      final public String getName() {
        return name();
      }

      public String name() {
        return name;
      }

      public String name;

      final public FieldDef setType(Type type) {
        return type(type);
      }

      public FieldDef type(Type type) {
        this.type = type;
        return this;
      }

      final public Type getType() {
        return type();
      }

      public Type type() {
        return type;
      }

      public Type type;

      final public FieldDef setInitializer(GazelleV_LeftArrowScript.Evaluable initializer) {
        return initializer(initializer);
      }

      public FieldDef initializer(GazelleV_LeftArrowScript.Evaluable initializer) {
        this.initializer = initializer;
        return this;
      }

      final public GazelleV_LeftArrowScript.Evaluable getInitializer() {
        return initializer();
      }

      public GazelleV_LeftArrowScript.Evaluable initializer() {
        return initializer;
      }

      public GazelleV_LeftArrowScript.Evaluable initializer;

      public Set<String> modifiers;

      transient public FieldGen bcel;

      public boolean hasModifier(String modifier) {
        return contains(modifiers, modifier);
      }

      public void addModifier(String modifier) {
        modifiers = createOrAddToSet(modifiers, modifier);
      }

      public boolean isStatic() {
        return hasModifier("static");
      }
    }

    public String structForHash() {
      return GazelleV_LeftArrowScript.scriptStruct(litorderedmap("userGivenName", userGivenName, "fields", fields, "methods", methods, "fullCompilation", fullCompilation));
    }

    public String classHash_cache;

    public String classHash() {
      if (classHash_cache == null)
        classHash_cache = classHash_load();
      return classHash_cache;
    }

    public String classHash_load() {
      String struct = structForHash();
      if (verbose)
        print("structForHash", struct);
      return md5(struct);
    }

    public String finalClassName() {
      return classDefPrefix() + finalClassNameWithoutPrefix();
    }

    public String finalClassNameWithoutPrefix() {
      return or2(userGivenName, "C") + "_" + classHash();
    }

    public byte[] toBytes_cache;

    public byte[] toBytes() {
      if (toBytes_cache == null)
        toBytes_cache = toBytes_load();
      return toBytes_cache;
    }

    public byte[] toBytes_load() {
      ClassMaker classMaker = new ClassMaker(finalClassName(), className(typeToClass(superClass)), mapToStringArray(interfaces, i -> className(typeToClass(i))));
      var cp = classMaker.getConstantPool();
      for (var field : fields) {
        var type = field.type;
        var fg = new FieldGen(Const.ACC_PUBLIC, typeToBCELType(type), field.name, cp);
        fg.isTransient(field.hasModifier("transient"));
        fg.isStatic(field.isStatic());
        if (type instanceof ParameterizedType)
          fg.addAttribute(new org.apache.bcel.classfile.Signature(cp.addUtf8("Signature"), 2, cp.addUtf8(typeToVMSignature((ParameterizedType) type)), cp.getConstantPool()));
        classMaker.addField(fg);
        field.bcel = fg;
      }
      if (nempty(initializers)) {
        addMethod(initializerMethod = new GazelleV_LeftArrowScript.FunctionDef("$initFields", new String[0], new GazelleV_LeftArrowScript.Script(initializers)).returnType(void.class));
        if (!hasUserDefinedDefaultConstructor())
          addMethod(new GazelleV_LeftArrowScript.FunctionDef("<init>", new String[0], new GazelleV_LeftArrowScript.Script(emptyList())).returnType(void.class));
      }
      for (var method : methods) if (fullCompilation)
        fullyCompileMethod(classMaker, method);
      else
        semiCompileMethod(classMaker, method);
      if (!hasUserDefinedDefaultConstructor())
        classMaker.addDefaultConstructor();
      if (srcRef() != null)
        classMaker.addField(new FieldGen(Const.ACC_PUBLIC | Const.ACC_STATIC, classToBCELType(TokenRangeWithSrc.class), srcRefField(), classMaker.getConstantPool()));
      return classMaker.toBytes();
    }

    public TokenRangeWithSrc srcRef() {
      return tokenRangeWithSrc();
    }

    public void addToInitializerMethod(FieldDef field) {
      if (field.isStatic())
        return;
      isFieldInitializer.set(l(initializers));
      initializers.add(new GazelleV_LeftArrowScript.SetField(new GazelleV_LeftArrowScript.GetVar("this"), field.name, field.initializer));
    }

    public boolean hasUserDefinedDefaultConstructor() {
      return any(methods, m -> m.isConstructor() && empty(m.args));
    }

    public void semiCompileMethod(ClassMaker classMaker, GazelleV_LeftArrowScript.FunctionDef method) {
      int iMethod = l(methodBodies);
      methodBodies.add(method);
      String bodyFieldName = "_body" + iMethod;
      classMaker.addField(new FieldGen(Const.ACC_PUBLIC | Const.ACC_STATIC, classToBCELType(GazelleV_LeftArrowScript.Evaluable.class), bodyFieldName, classMaker.getConstantPool()));
      int nArgs = l(method.args);
      MethodMaker mm = new MethodMaker(classMaker.cg(), Const.ACC_PUBLIC, typeToBCEL(method.returnType), method.name, mapToArray(__50 -> typeToBCEL(__50), method.argTypes));
      int iThis = 0, iCtx = mm.frameSize;
      if (method.isConstructor()) {
        mm.aload(iThis);
        mm.invokeConstructor(typeToClass(superClass));
        if (initializerMethod != null) {
          mm.aload(iThis);
          mm.il.append(mm.factory.createInvoke(classMaker.className(), initializerMethod.name, mm.wrapType(void.class), mm.wrapTypes(new Class[0]), Const.INVOKESPECIAL));
        }
      }
      mm.newObject(FlexibleVarContext.class);
      mm.astore(iCtx);
      mm.aload(iCtx);
      mm.stringConstant("this");
      mm.aload(iThis);
      mm.invokeVirtual(VarContext.class, void.class, "put", String.class, Object.class);
      for (int iArg = 0; iArg < nArgs; iArg++) {
        mm.aload(iCtx);
        mm.stringConstant(method.args[iArg]);
        mm.aloadArgWithAutoboxing(iArg);
        mm.invokeVirtual(VarContext.class, void.class, "put", String.class, Object.class);
      }
      mm.getStaticField(classMaker.className(), bodyFieldName, GazelleV_LeftArrowScript.Evaluable.class);
      mm.aload(iCtx);
      mm.invokeInterface(GazelleV_LeftArrowScript.Evaluable.class, Object.class, "get", VarContext.class);
      Type type = method.returnType();
      if (type == void.class)
        mm._return();
      else {
        if (type instanceof Class && isPrimitiveType((Class) type)) {
          Class c = (Class) type;
          Class boxed = primitiveToBoxedType(c);
          mm.checkCast(typeToBCEL(boxed));
          mm.invokeVirtual(boxed, c, c + "Value");
          mm.returnPrimitive(c);
        } else {
          if (type != Object.class)
            mm.checkCast(typeToBCEL(type));
          mm.areturn();
        }
      }
      mm.done();
    }

    public void fullyCompileMethod(ClassMaker classMaker, GazelleV_LeftArrowScript.FunctionDef method) {
      MethodMaker mm = new MethodMaker(classMaker, typeToClass(method.returnType()), method.name, repArray(Class.class, Object.class, l(method.args)));
      var tbc = new LASToByteCode(mm) {

        public JVMStackCellType compileGetVar(GazelleV_LeftArrowScript.GetVar code) {
          if (eq(code.var, "this")) {
            mm.aload(0);
            return JVMStackCellType.objValue;
          }
          int iArg = indexOf(method.args, code.var);
          if (iArg >= 0) {
            mm.aload(iArg + 1);
            return JVMStackCellType.objValue;
          }
          return super.compileGetVar(code);
        }
      };
      tbc.postConversion = stackTop -> mm.convertToObject(stackTop);
      tbc.compileScript(method.body);
      mm.areturn();
      mm.done();
    }

    public void init(Class c) {
      if (resolvedClass != null)
        return;
      resolvedClass = c;
      for (int iMethod = 0; iMethod < l(methodBodies); iMethod++) {
        String bodyFieldName = "_body" + iMethod;
        set(c, bodyFieldName, methodBodies.get(iMethod).body);
      }
      for (var field : fields) if (field.isStatic() && field.initializer != null)
        set(c, field.name, field.initializer.get());
      setOpt(c, srcRefField(), srcRef());
    }

    public ResolvableLASClass resolvable(ILASClassLoader lasClassLoader) {
      return new ResolvableLASClass(lasClassLoader, this);
    }

    public Object typeToBCEL(LASValueDescriptor descriptor) {
      return or(descriptor == null ? null : descriptor.javaClass(), Object.class);
    }

    public Object typeToBCEL(Type type) {
      {
        var __1 = resolvableClassToName(type);
        if (__1 != null)
          return __1;
      }
      return typeToClass(type);
    }

    public void addField(FieldDef field) {
      fields.add(field);
      fieldsByName.put(field.name, field);
      if (field.initializer != null)
        addToInitializerMethod(field);
    }

    public void addMethod(GazelleV_LeftArrowScript.FunctionDef method) {
      methods.add(method);
      methodsByName.put(method.name, method);
    }

    public String srcRefField() {
      return "__srcRef";
    }
  }

  static public class SynchronizedCollection<E> implements Collection<E>, Serializable {

    public SynchronizedCollection() {
    }

    @java.io.Serial
    static final public long serialVersionUID = 3053995032091335093L;

    @SuppressWarnings("serial")
    public Collection<E> c;

    @SuppressWarnings("serial")
    public Object mutex;

    public SynchronizedCollection(Collection<E> c) {
      this.c = Objects.requireNonNull(c);
      mutex = this;
    }

    public SynchronizedCollection(Collection<E> c, Object mutex) {
      this.c = Objects.requireNonNull(c);
      this.mutex = Objects.requireNonNull(mutex);
    }

    public int size() {
      synchronized (mutex) {
        return c.size();
      }
    }

    public boolean isEmpty() {
      synchronized (mutex) {
        return c.isEmpty();
      }
    }

    public boolean contains(Object o) {
      synchronized (mutex) {
        return c.contains(o);
      }
    }

    public Object[] toArray() {
      synchronized (mutex) {
        return c.toArray();
      }
    }

    public <T> T[] toArray(T[] a) {
      synchronized (mutex) {
        return c.toArray(a);
      }
    }

    public <T> T[] toArray(java.util.function.IntFunction<T[]> f) {
      synchronized (mutex) {
        return c.toArray(f);
      }
    }

    public Iterator<E> iterator() {
      return c.iterator();
    }

    public boolean add(E e) {
      synchronized (mutex) {
        return c.add(e);
      }
    }

    public boolean remove(Object o) {
      synchronized (mutex) {
        return c.remove(o);
      }
    }

    public boolean containsAll(Collection<?> coll) {
      synchronized (mutex) {
        return c.containsAll(coll);
      }
    }

    public boolean addAll(Collection<? extends E> coll) {
      synchronized (mutex) {
        return c.addAll(coll);
      }
    }

    public boolean removeAll(Collection<?> coll) {
      synchronized (mutex) {
        return c.removeAll(coll);
      }
    }

    public boolean retainAll(Collection<?> coll) {
      synchronized (mutex) {
        return c.retainAll(coll);
      }
    }

    public void clear() {
      synchronized (mutex) {
        c.clear();
      }
    }

    public String toString() {
      synchronized (mutex) {
        return c.toString();
      }
    }

    @Override
    public void forEach(java.util.function.Consumer<? super E> consumer) {
      synchronized (mutex) {
        c.forEach(consumer);
      }
    }

    @Override
    public boolean removeIf(java.util.function.Predicate<? super E> filter) {
      synchronized (mutex) {
        return c.removeIf(filter);
      }
    }

    @Override
    public Spliterator<E> spliterator() {
      return c.spliterator();
    }

    @Override
    public java.util.stream.Stream<E> stream() {
      return c.stream();
    }

    @Override
    public java.util.stream.Stream<E> parallelStream() {
      return c.parallelStream();
    }

    @java.io.Serial
    private void writeObject(ObjectOutputStream s) throws IOException {
      synchronized (mutex) {
        s.defaultWriteObject();
      }
    }
  }

  static public class SynchronizedRandomAccessList<E> extends SynchronizedList<E> implements RandomAccess {

    public SynchronizedRandomAccessList() {
    }

    public SynchronizedRandomAccessList(List<E> list) {
      super(list);
    }

    public SynchronizedRandomAccessList(List<E> list, Object mutex) {
      super(list, mutex);
    }

    public List<E> subList(int fromIndex, int toIndex) {
      synchronized (mutex) {
        return new SynchronizedRandomAccessList<>(list.subList(fromIndex, toIndex), mutex);
      }
    }

    @java.io.Serial
    static final public long serialVersionUID = 1530674583602358482L;

    @java.io.Serial
    private Object writeReplace() {
      return new SynchronizedList<>(list);
    }
  }

  static public class HasTokenRangeWithSrc implements IHasTokenRangeWithSrc {

    final public HasTokenRangeWithSrc setTokenRangeWithSrc(TokenRangeWithSrc tokenRangeWithSrc) {
      return tokenRangeWithSrc(tokenRangeWithSrc);
    }

    public HasTokenRangeWithSrc tokenRangeWithSrc(TokenRangeWithSrc tokenRangeWithSrc) {
      this.tokenRangeWithSrc = tokenRangeWithSrc;
      return this;
    }

    final public TokenRangeWithSrc getTokenRangeWithSrc() {
      return tokenRangeWithSrc();
    }

    public TokenRangeWithSrc tokenRangeWithSrc() {
      return tokenRangeWithSrc;
    }

    public TokenRangeWithSrc tokenRangeWithSrc;
  }

  static public class SimpleLeftToRightParser extends Meta {

    public String text;

    public List<String> tok;

    final public ListAndIndex<String> getPtr() {
      return ptr();
    }

    public ListAndIndex<String> ptr() {
      return ptr;
    }

    public ListAndIndex<String> ptr;

    public ListAndIndex<String> mainLoopPtr;

    public String currentToken;

    public boolean caseInsensitive = false;

    public List warnings = new ArrayList();

    final public SimpleLeftToRightParser setInternIdentifiers(boolean internIdentifiers) {
      return internIdentifiers(internIdentifiers);
    }

    public SimpleLeftToRightParser internIdentifiers(boolean internIdentifiers) {
      this.internIdentifiers = internIdentifiers;
      return this;
    }

    final public boolean getInternIdentifiers() {
      return internIdentifiers();
    }

    public boolean internIdentifiers() {
      return internIdentifiers;
    }

    public boolean internIdentifiers = false;

    public SimpleLeftToRightParser() {
    }

    public SimpleLeftToRightParser(String text) {
      this.text = text;
    }

    public SimpleLeftToRightParser(List<String> tok) {
      this.tok = tok;
    }

    transient public IF1<String, List<String>> tokenize;

    public List<String> tokenize(String text) {
      return tokenize != null ? tokenize.get(text) : tokenize_base(text);
    }

    final public List<String> tokenize_fallback(IF1<String, List<String>> _f, String text) {
      return _f != null ? _f.get(text) : tokenize_base(text);
    }

    public List<String> tokenize_base(String text) {
      return javaTok(text);
    }

    final public String token() {
      return t();
    }

    public String t() {
      return currentToken;
    }

    public String token(int i) {
      return get(tok, ptr.idx() + i * 2);
    }

    final public String consume() {
      return next();
    }

    final public String tpp() {
      return next();
    }

    public String next() {
      var t = t();
      next(1);
      return t;
    }

    final public String prevSpace() {
      return lastSpace();
    }

    public String lastSpace() {
      return get(tok, ptr.idx() - 1);
    }

    public String nextSpace() {
      return get(tok, ptr.idx() + 1);
    }

    public String space(int i) {
      return get(tok, ptr.idx() + i * 2 + 1);
    }

    public void unconsume() {
      next(-1);
    }

    final public boolean eqTok(String a, String b) {
      return tokEq(a, b);
    }

    public boolean tokEq(String a, String b) {
      return eqOrEqic(caseInsensitive, a, b);
    }

    public boolean tokEqOneOf(String a, String... l) {
      return any(l, b -> tokEq(a, b));
    }

    public boolean is(int i, String t) {
      return tokEq(token(i), t);
    }

    public boolean is(String t) {
      return tokEq(currentToken, t);
    }

    public boolean was(String t) {
      return tokEq(token(-1), t);
    }

    public boolean isOneOf(String... tokens) {
      return tokEqOneOf(currentToken, tokens);
    }

    public String[] consumeArray(int n) {
      String[] array = new String[n];
      for (int i = 0; i < n; i++) array[i] = consume();
      return array;
    }

    public boolean isInteger() {
      return isInteger(t());
    }

    public boolean isInteger(String s) {
      return main.isInteger(s);
    }

    public boolean isIdentifier() {
      return isIdentifier(t());
    }

    public boolean isIdentifier(String s) {
      return main.isIdentifier(s);
    }

    public String consumeIdentifier() {
      String s = assertIdentifier(consume());
      if (internIdentifiers)
        s = intern(s);
      return s;
    }

    public String consumeIdentifierOpt() {
      return isIdentifier(t()) ? consumeIdentifier() : null;
    }

    public int consumeInteger() {
      return parseInt(assertInteger(consume()));
    }

    public boolean consumeOpt(String token) {
      if (!is(token))
        return false;
      consume();
      return true;
    }

    public void consume(String token) {
      if (!is(token))
        throw fail("Expected " + quote(token) + ", got " + describeToken(token()));
      consume();
    }

    public String describeToken(String token) {
      return token == null ? "EOF" : quote(token);
    }

    public String consumeOneOf(String... tokens) {
      if (!isOneOf(tokens))
        throw fail("Expected one of " + asList(tokens));
      return consume();
    }

    public void ptr(ListAndIndex<String> ptr) {
      this.ptr = ptr;
      fetch();
    }

    final public int tokIdx() {
      return idx();
    }

    public int idx() {
      return ptr.idx();
    }

    public int lTok() {
      return l(tok);
    }

    public int nRemainingTokens() {
      return (lTok() - idx()) / 2;
    }

    final public boolean endOfText() {
      return atEnd();
    }

    public boolean atEnd() {
      return ptr.atEnd();
    }

    public void fetch() {
      currentToken = ptr.get();
    }

    public boolean lineBreak() {
      return containsLineBreak(get(tok, ptr.idx() - 1));
    }

    public boolean atEndOrLineBreak() {
      return atEnd() || lineBreak();
    }

    public void init() {
      if (tok == null)
        tok = tokenize(text);
      if (ptr == null)
        ptr(new ListAndIndex(tok, 1));
    }

    public boolean mainLoop() {
      init();
      if (atEnd())
        return false;
      if (eq(mainLoopPtr, ptr))
        throw fail("main loop didn't advance (current token: " + quote(token()) + ")");
      mainLoopPtr = ptr;
      return true;
    }

    public class AssureAdvance {

      public ListAndIndex<String> cur;

      {
        init();
      }

      public boolean get() {
        if (atEnd())
          return false;
        if (eq(cur, ptr))
          throw fail("Parse loop didn't advance (current token: " + quote(token()) + ")");
        cur = ptr;
        return true;
      }
    }

    public void unknownToken() {
      warn("Unknown token: " + t());
    }

    public void warn(String msg) {
      warnings.add(print(msg));
    }

    final public void skip(int n) {
      next(n);
    }

    final public void consume(int n) {
      next(n);
    }

    public void next(int n) {
      ptr(min(lTok(), ptr.idx() + n * 2));
    }

    public void ptr(int i) {
      ptr(new ListAndIndex(tok, min(i | 1, l(tok))));
    }

    public LineAndColumn lineAndColumn() {
      return tokenToLineAndColumn(ptr);
    }

    public LineAndColumn lineAndColumn(int idx) {
      return tokenToLineAndColumn(ptr.plus(idx * 2));
    }

    public String consumeUntilSpaceOr(IF0<Boolean> pred) {
      int i = idx();
      do next(); while (!atEnd() && empty(lastSpace()) && !pred.get());
      return joinSubList(tok, i, idx() - 1);
    }

    public void setText(String text) {
      this.text = text;
      tok = null;
      ptr = null;
    }

    public int relativeIndexOf(String token) {
      int n = nRemainingTokens();
      for (int i = 0; i < n; i++) if (eqTok(token(i), token))
        return i;
      return -1;
    }
  }

  public interface IMultiMap<A, B> {

    public Set<A> keySet();

    public Collection<B> get(A a);

    public int size();

    public int keyCount();
  }

  static public class ResolvableLASClass implements IResolvableClass {

    public ResolvableLASClass() {
    }

    transient public ILASClassLoader lasClassLoader;

    public LASClassDef classDef;

    public ResolvableLASClass(ILASClassLoader lasClassLoader, LASClassDef classDef) {
      this.classDef = classDef;
      this.lasClassLoader = lasClassLoader;
    }

    public String resolveToClassName() {
      return classDef.finalClassName();
    }

    public Class resolvedClass() {
      return classDef.resolvedClass;
    }

    final public Class resolveToClass() {
      return get();
    }

    public Class get() {
      if (resolvedClass() == null) {
        if (lasClassLoader == null)
          throw fail("Need LASClassLoader to define " + classDef.userGivenName);
        Class resolvedClass = lasClassLoader.defineLASClass(classDef.finalClassName(), () -> classDef.toBytes());
        classDef.init(resolvedClass);
      }
      return resolvedClass();
    }

    public String toString() {
      if (resolvedClass() != null)
        return className(resolvedClass());
      {
        var __1 = userGivenName();
        if (__1 != null)
          return __1;
      }
      if (classDef.classHash_cache != null)
        return classDef.finalClassNameWithoutPrefix();
      return or2(userGivenName(), "script-defined class") + " [unresolved]";
    }

    public String userGivenName() {
      return classDef.userGivenName;
    }
  }

  static public interface ISetter<A> {

    public void set(A a);
  }

  abstract static public class VarContext {

    final public VarContext getParent() {
      return parent();
    }

    public VarContext parent() {
      return parent;
    }

    public VarContext parent;

    public VarContext() {
    }

    public VarContext(VarContext parent) {
      this.parent = parent;
    }

    abstract public Object get(String name);

    final public void put(String name, Object value) {
      set(name, value);
    }

    abstract public void set(String name, Object value);

    final public AutoCloseable tempPut(String name, Object value) {
      return tempSet(name, value);
    }

    abstract public AutoCloseable tempSet(String name, Object value);

    abstract public void unset(String name);

    abstract public Map<String, Object> varMap();

    public void printMe() {
      pnl(varMap());
      print("parent", parent);
    }

    final public VarContext setExitFromScript(Object exitFromScript) {
      return exitFromScript(exitFromScript);
    }

    public VarContext exitFromScript(Object exitFromScript) {
      this.exitFromScript = exitFromScript;
      return this;
    }

    final public Object getExitFromScript() {
      return exitFromScript();
    }

    public Object exitFromScript() {
      return exitFromScript;
    }

    public Object exitFromScript;

    final public VarContext setReturnValue(Object returnValue) {
      return returnValue(returnValue);
    }

    public VarContext returnValue(Object returnValue) {
      this.returnValue = returnValue;
      return this;
    }

    final public Object getReturnValue() {
      return returnValue();
    }

    public Object returnValue() {
      return returnValue;
    }

    public Object returnValue;

    public boolean exiting() {
      ping();
      return exitFromScript != null;
    }
  }

  static public class MapI<A, B> extends IterableIterator<B> {

    public IF1<A, B> f;

    public Iterator<A> it;

    public MapI(IF1<A, B> f, Iterator<A> it) {
      this.it = it;
      this.f = f;
    }

    public boolean hasNext() {
      return it.hasNext();
    }

    public B next() {
      return f.get(it.next());
    }
  }

  static public class UnsynchronizedCompactHashSet<A> extends java.util.AbstractSet<A> {

    final static public int INITIAL_SIZE = 3;

    public final static double LOAD_FACTOR = 0.75;

    final static public Object nullObject = new Object();

    final static public Object deletedObject = new Object();

    public int elements;

    public int freecells;

    public A[] objects;

    public int modCount;

    public UnsynchronizedCompactHashSet() {
      this(INITIAL_SIZE);
    }

    public UnsynchronizedCompactHashSet(int size) {
      objects = (A[]) new Object[(size == 0 ? 1 : size)];
      elements = 0;
      freecells = objects.length;
      modCount = 0;
    }

    public UnsynchronizedCompactHashSet(Collection<A> c) {
      this(c.size());
      addAll(c);
    }

    @Override
    public Iterator<A> iterator() {
      return new CompactHashIterator<A>();
    }

    @Override
    public int size() {
      return elements;
    }

    @Override
    public boolean isEmpty() {
      return elements == 0;
    }

    @Override
    public boolean contains(Object o) {
      return find(o) != null;
    }

    public A find(Object o) {
      if (o == null)
        o = nullObject;
      int hash = o.hashCode();
      int index = (hash & 0x7FFFFFFF) % objects.length;
      int offset = 1;
      while (objects[index] != null && !(objects[index].hashCode() == hash && objects[index].equals(o))) {
        index = ((index + offset) & 0x7FFFFFFF) % objects.length;
        offset = offset * 2 + 1;
        if (offset == -1)
          offset = 2;
      }
      return objects[index];
    }

    public boolean removeIfSame(Object o) {
      A value = find(o);
      if (value == o) {
        remove(value);
        return true;
      }
      return false;
    }

    @Override
    public boolean add(Object o) {
      if (o == null)
        o = nullObject;
      int hash = o.hashCode();
      int index = (hash & 0x7FFFFFFF) % objects.length;
      int offset = 1;
      int deletedix = -1;
      while (objects[index] != null && !(objects[index].hashCode() == hash && objects[index].equals(o))) {
        if (objects[index] == deletedObject)
          deletedix = index;
        index = ((index + offset) & 0x7FFFFFFF) % objects.length;
        offset = offset * 2 + 1;
        if (offset == -1)
          offset = 2;
      }
      if (objects[index] == null) {
        if (deletedix != -1)
          index = deletedix;
        else
          freecells--;
        modCount++;
        elements++;
        objects[index] = (A) o;
        if (1 - (freecells / (double) objects.length) > LOAD_FACTOR)
          rehash();
        return true;
      } else
        return false;
    }

    @Override
    public boolean remove(Object o) {
      if (o == null)
        o = nullObject;
      int hash = o.hashCode();
      int index = (hash & 0x7FFFFFFF) % objects.length;
      int offset = 1;
      while (objects[index] != null && !(objects[index].hashCode() == hash && objects[index].equals(o))) {
        index = ((index + offset) & 0x7FFFFFFF) % objects.length;
        offset = offset * 2 + 1;
        if (offset == -1)
          offset = 2;
      }
      if (objects[index] != null) {
        objects[index] = (A) deletedObject;
        modCount++;
        elements--;
        return true;
      } else
        return false;
    }

    @Override
    public void clear() {
      elements = 0;
      for (int ix = 0; ix < objects.length; ix++) objects[ix] = null;
      freecells = objects.length;
      modCount++;
    }

    @Override
    public Object[] toArray() {
      Object[] result = new Object[elements];
      Object[] objects = this.objects;
      int pos = 0;
      for (int i = 0; i < objects.length; i++) if (objects[i] != null && objects[i] != deletedObject) {
        if (objects[i] == nullObject)
          result[pos++] = null;
        else
          result[pos++] = objects[i];
      }
      return result;
    }

    @Override
    public <T> T[] toArray(T[] a) {
      int size = elements;
      if (a.length < size)
        a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
      A[] objects = this.objects;
      int pos = 0;
      for (int i = 0; i < objects.length; i++) if (objects[i] != null && objects[i] != deletedObject) {
        if (objects[i] == nullObject)
          a[pos++] = null;
        else
          a[pos++] = (T) objects[i];
      }
      return a;
    }

    public void rehash() {
      int garbagecells = objects.length - (elements + freecells);
      if (garbagecells / (double) objects.length > 0.05)
        rehash(objects.length);
      else
        rehash(objects.length * 2 + 1);
    }

    public void rehash(int newCapacity) {
      int oldCapacity = objects.length;
      @SuppressWarnings("unchecked")
      A[] newObjects = (A[]) new Object[newCapacity];
      for (int ix = 0; ix < oldCapacity; ix++) {
        Object o = objects[ix];
        if (o == null || o == deletedObject)
          continue;
        int hash = o.hashCode();
        int index = (hash & 0x7FFFFFFF) % newCapacity;
        int offset = 1;
        while (newObjects[index] != null) {
          index = ((index + offset) & 0x7FFFFFFF) % newCapacity;
          offset = offset * 2 + 1;
          if (offset == -1)
            offset = 2;
        }
        newObjects[index] = (A) o;
      }
      objects = newObjects;
      freecells = objects.length - elements;
    }

    public class CompactHashIterator<T> implements Iterator<T> {

      public int index;

      public int lastReturned = -1;

      public int expectedModCount;

      @SuppressWarnings("empty-statement")
      public CompactHashIterator() {
        for (index = 0; index < objects.length && (objects[index] == null || objects[index] == deletedObject); index++) ;
        expectedModCount = modCount;
      }

      @Override
      public boolean hasNext() {
        return index < objects.length;
      }

      @SuppressWarnings("empty-statement")
      @Override
      public T next() {
        int length = objects.length;
        if (index >= length) {
          lastReturned = -2;
          throw new NoSuchElementException();
        }
        lastReturned = index;
        for (index += 1; index < length && (objects[index] == null || objects[index] == deletedObject); index++) ;
        if (objects[lastReturned] == nullObject)
          return null;
        else
          return (T) objects[lastReturned];
      }

      @Override
      public void remove() {
        if (modCount != expectedModCount)
          throw new ConcurrentModificationException();
        if (lastReturned == -1 || lastReturned == -2)
          throw new IllegalStateException();
        if (objects[lastReturned] != null && objects[lastReturned] != deletedObject) {
          objects[lastReturned] = (A) deletedObject;
          elements--;
          modCount++;
          expectedModCount = modCount;
        }
      }
    }

    public int capacity() {
      return objects.length;
    }

    public boolean shrinkToFactor(double factor) {
      if (factor > LOAD_FACTOR)
        throw fail("Shrink factor must be equal to or smaller than load factor: " + factor + " / " + LOAD_FACTOR);
      int newCapacity = max(INITIAL_SIZE, iround(size() / factor));
      if (newCapacity >= capacity())
        return false;
      rehash(newCapacity);
      return true;
    }
  }

  static public class FailedRule extends RuleWithParams {

    public List<Exp> satisfiedConditions;

    public Exp remainingCondition;

    public FailedRule() {
    }

    public FailedRule(IfThen rule, VarMatches matches, Exp remainingCondition) {
      this.remainingCondition = remainingCondition;
      this.matches = matches;
      this.rule = rule;
    }

    public FailedRule(IfThen rule, VarMatches matches, List<Exp> satisfiedConditions, Exp remainingCondition) {
      this.remainingCondition = remainingCondition;
      this.satisfiedConditions = satisfiedConditions;
      this.matches = matches;
      this.rule = rule;
    }
  }

  static public class RuleWithParams implements IFieldsToList {

    static final public String _fieldOrder = "rule matches";

    public IfThen rule;

    public VarMatches matches;

    public RuleWithParams() {
    }

    public RuleWithParams(IfThen rule, VarMatches matches) {
      this.matches = matches;
      this.rule = rule;
    }

    public String toString() {
      return shortClassName_dropNumberPrefix(this) + "(" + rule + ", " + matches + ")";
    }

    public boolean equals(Object o) {
      if (!(o instanceof RuleWithParams))
        return false;
      RuleWithParams __7 = (RuleWithParams) o;
      return eq(rule, __7.rule) && eq(matches, __7.matches);
    }

    public int hashCode() {
      int h = 1254104616;
      h = boostHashCombine(h, _hashCode(rule));
      h = boostHashCombine(h, _hashCode(matches));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { rule, matches };
    }

    public String ruleID() {
      return rule.globalID;
    }
  }

  static public class IfThen implements IFieldsToList {

    static final public String _fieldOrder = "in out globalID options originalText";

    public Exp in;

    public Exp out;

    public IfThen() {
    }

    public IfThen(Exp in, Exp out) {
      this.out = out;
      this.in = in;
    }

    public boolean equals(Object o) {
      if (!(o instanceof IfThen))
        return false;
      IfThen __8 = (IfThen) o;
      return eq(in, __8.in) && eq(out, __8.out);
    }

    public int hashCode() {
      int h = -2108234502;
      h = boostHashCombine(h, _hashCode(in));
      h = boostHashCombine(h, _hashCode(out));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { in, out };
    }

    public String globalID;

    public Set<String> options;

    public String originalText;

    public String text() {
      Collection<String> opt = options;
      if (nempty(globalID))
        opt = concatLists(ll("id: " + globalID), opt);
      return (nempty(opt) ? "[" + joinWithComma(opt) + "] " : "") + (in == null ? "" : in.text() + "\n  => ") + nlLogic_text(out);
    }

    public String toString() {
      return text();
    }
  }

  abstract static public class Exp {

    abstract public String text();

    public String toString() {
      return text();
    }
  }

  static public class Func extends Exp implements IFieldsToList {

    public String name;

    public Exp arg;

    public Func() {
    }

    public Func(String name, Exp arg) {
      this.arg = arg;
      this.name = name;
    }

    public boolean equals(Object o) {
      if (!(o instanceof Func))
        return false;
      Func __0 = (Func) o;
      return eq(name, __0.name) && eq(arg, __0.arg);
    }

    public int hashCode() {
      int h = 2201316;
      h = boostHashCombine(h, _hashCode(name));
      h = boostHashCombine(h, _hashCode(arg));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { name, arg };
    }

    public List<String> options;

    public Func(String name, List<String> options, Exp arg) {
      this.arg = arg;
      this.options = options;
      this.name = name;
    }

    public String text() {
      return name + (empty(options) ? "" : "[" + joinWithComma(options) + "]") + "(" + arg.text() + ")";
    }

    public String argText() {
      return nlLogic_text(arg);
    }
  }

  static public class And extends Exp implements IFieldsToList {

    public Exp a;

    public Exp b;

    public And() {
    }

    public And(Exp a, Exp b) {
      this.b = b;
      this.a = a;
    }

    public boolean equals(Object o) {
      if (!(o instanceof And))
        return false;
      And __1 = (And) o;
      return eq(a, __1.a) && eq(b, __1.b);
    }

    public int hashCode() {
      int h = 65975;
      h = boostHashCombine(h, _hashCode(a));
      h = boostHashCombine(h, _hashCode(b));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { a, b };
    }

    public String text() {
      return a.text() + "\n  && " + b.text();
    }
  }

  static public class ExpNot extends Exp implements IFieldsToList {

    public Exp a;

    public ExpNot() {
    }

    public ExpNot(Exp a) {
      this.a = a;
    }

    public boolean equals(Object o) {
      if (!(o instanceof ExpNot))
        return false;
      ExpNot __2 = (ExpNot) o;
      return eq(a, __2.a);
    }

    public int hashCode() {
      int h = 2089649046;
      h = boostHashCombine(h, _hashCode(a));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { a };
    }

    public String text() {
      return "!" + a.text();
    }
  }

  abstract static public class Literal extends Exp {
  }

  static public class Sentence extends Literal implements IFieldsToList {

    public List<String> tok;

    public Sentence() {
    }

    public Sentence(List<String> tok) {
      this.tok = tok;
    }

    public boolean equals(Object o) {
      if (!(o instanceof Sentence))
        return false;
      Sentence __3 = (Sentence) o;
      return eq(tok, __3.tok);
    }

    public int hashCode() {
      int h = 1327381123;
      h = boostHashCombine(h, _hashCode(tok));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { tok };
    }

    public String text() {
      return join(tok);
    }
  }

  static public class Sentence2 extends Literal implements IFieldsToList {

    public String text;

    public Sentence2() {
    }

    public Sentence2(String text) {
      this.text = text;
    }

    public boolean equals(Object o) {
      if (!(o instanceof Sentence2))
        return false;
      Sentence2 __4 = (Sentence2) o;
      return eq(text, __4.text);
    }

    public int hashCode() {
      int h = -1800858097;
      h = boostHashCombine(h, _hashCode(text));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { text };
    }

    public String text() {
      return text;
    }
  }

  static public class Eq extends Exp implements IFieldsToList {

    public Exp left;

    public Exp right;

    public Eq() {
    }

    public Eq(Exp left, Exp right) {
      this.right = right;
      this.left = left;
    }

    public boolean equals(Object o) {
      if (!(o instanceof Eq))
        return false;
      Eq __5 = (Eq) o;
      return eq(left, __5.left) && eq(right, __5.right);
    }

    public int hashCode() {
      int h = 2252;
      h = boostHashCombine(h, _hashCode(left));
      h = boostHashCombine(h, _hashCode(right));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { left, right };
    }

    public String text() {
      return left.text() + " = " + right.text();
    }
  }

  static public class MethodOnObject implements IFieldsToList {

    static final public String _fieldOrder = "object method";

    transient public Object object;

    public String method;

    public MethodOnObject() {
    }

    public MethodOnObject(Object object, String method) {
      this.method = method;
      this.object = object;
    }

    public String toString() {
      return shortClassName_dropNumberPrefix(this) + "(" + object + ", " + method + ")";
    }

    public boolean equals(Object o) {
      if (!(o instanceof MethodOnObject))
        return false;
      MethodOnObject __1 = (MethodOnObject) o;
      return eq(object, __1.object) && eq(method, __1.method);
    }

    public int hashCode() {
      int h = 791808543;
      h = boostHashCombine(h, _hashCode(object));
      h = boostHashCombine(h, _hashCode(method));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { object, method };
    }
  }

  static abstract public class CloseableIterableIterator<A> extends IterableIterator<A> implements AutoCloseable {

    public void close() throws Exception {
    }
  }

  public interface ISleeper_v2 {

    default public Sleeping doLater(long targetTime, Runnable r) {
      return doLater(sysTimeToTimestamp(targetTime), r);
    }

    public Sleeping doLater(Timestamp targetTime, Runnable r);

    public default Sleeping doAfter(double seconds, Runnable r) {
      return doLater(tsNow().plusSeconds(seconds), r);
    }
  }

  public interface IHasTokenRangeWithSrc {

    public IHasTokenRangeWithSrc setTokenRangeWithSrc(TokenRangeWithSrc src);

    public TokenRangeWithSrc tokenRangeWithSrc();

    default public String srcText() {
      var src = tokenRangeWithSrc();
      return src == null ? null : src.text();
    }
  }

  static public class Seconds implements Comparable<Seconds>, IFieldsToList {

    public double seconds;

    public Seconds() {
    }

    public Seconds(double seconds) {
      this.seconds = seconds;
    }

    public boolean equals(Object o) {
      if (!(o instanceof Seconds))
        return false;
      Seconds __1 = (Seconds) o;
      return seconds == __1.seconds;
    }

    public int hashCode() {
      int h = -660217249;
      h = boostHashCombine(h, _hashCode(seconds));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { seconds };
    }

    final public double get() {
      return seconds();
    }

    final public double getDouble() {
      return seconds();
    }

    public double seconds() {
      return seconds;
    }

    public String toString() {
      return formatDouble(seconds, 3) + " s";
    }

    public int compareTo(Seconds s) {
      return cmp(seconds, s.seconds);
    }

    public Seconds div(double x) {
      return new Seconds(get() / x);
    }

    public Seconds minus(Seconds x) {
      return new Seconds(get() - x.get());
    }
  }

  public interface IDoublePt {

    public double x_double();

    public double y_double();
  }

  static public interface IVar<A> extends IF0<A> {

    public void set(A a);

    public A get();

    default public Class<A> getType() {
      return null;
    }

    default public IF0<A> getter() {
      return () -> get();
    }

    default public IVF1<A> setter() {
      return __1 -> set(__1);
    }

    default public boolean has() {
      return get() != null;
    }

    default public void clear() {
      set(null);
    }
  }

  static public class MethodMaker {

    public ClassGen cg;

    public MethodGen mg;

    public InstructionList il = new InstructionList();

    public ConstantPoolGen cp;

    public InstructionFactory factory;

    public int frameSize;

    public org.apache.bcel.generic.Type[] argTypes;

    public int[] argStackIndex;

    final public MethodMaker setVerboseAdd(boolean verboseAdd) {
      return verboseAdd(verboseAdd);
    }

    public MethodMaker verboseAdd(boolean verboseAdd) {
      this.verboseAdd = verboseAdd;
      return this;
    }

    final public boolean getVerboseAdd() {
      return verboseAdd();
    }

    public boolean verboseAdd() {
      return verboseAdd;
    }

    public boolean verboseAdd = false;

    public boolean classConstantWorkaround = false;

    public MethodMaker(ClassMaker classMaker, Class returnType, String methodName, Class... argumentTypes) {
      this(classMaker.cg, returnType, methodName, argumentTypes);
    }

    public MethodMaker(ClassGen cg, Class returnType, String methodName, Class... argumentTypes) {
      this(cg, Const.ACC_PUBLIC, returnType, methodName, argumentTypes);
    }

    public MethodMaker(ClassGen cg, short modifiers, Object returnType, String methodName, Object... argumentTypes) {
      this.cg = cg;
      cp = cg.getConstantPool();
      factory = new InstructionFactory(cg);
      argTypes = wrapTypes(argumentTypes);
      mg = new MethodGen(modifiers, wrapType(returnType), argTypes, null, methodName, cg.getClassName(), il, cp);
      layoutStack();
    }

    public MethodMaker(ClassGen cg, short modifiers, Class returnType, String methodName, Class... argumentTypes) {
      this.cg = cg;
      cp = cg.getConstantPool();
      factory = new InstructionFactory(cg);
      argTypes = wrapTypes(argumentTypes);
      mg = new MethodGen(modifiers, wrapType(returnType), argTypes, null, methodName, cg.getClassName(), il, cp);
      layoutStack();
    }

    public void layoutStack() {
      frameSize = 1;
      argStackIndex = new int[l(argTypes)];
      for (int i = 0; i < l(argTypes); i++) {
        argStackIndex[i] = frameSize;
        frameSize += eqOneOf(argTypes[i], org.apache.bcel.generic.Type.LONG, org.apache.bcel.generic.Type.DOUBLE) ? 2 : 1;
      }
    }

    static public org.apache.bcel.generic.Type[] wrapTypes(Object[] classes) {
      org.apache.bcel.generic.Type[] types = new org.apache.bcel.generic.Type[l(classes)];
      for (int i = 0; i < l(classes); i++) types[i] = wrapType(classes[i]);
      return types;
    }

    static public org.apache.bcel.generic.Type wrapType(Object o) {
      if (o instanceof Class)
        return classToBCELType((Class) o);
      if (o instanceof String) {
        Class c = parsePrimitiveType((String) o);
        if (c != null)
          return classToBCELType(c);
        return new ObjectType((String) o);
      }
      return null;
    }

    public int newLocalVar() {
      return frameSize++;
    }

    public MethodMaker newObject(Class c, Class... argTypes) {
      il.append(factory.createNew(className(c)));
      il.append(InstructionConst.DUP);
      invokeConstructor(c, argTypes);
      return this;
    }

    public MethodMaker invokeConstructor(Class c, Class... argTypes) {
      Constructor ctor = findConstructor_precise_onTypes(c, argTypes);
      il.append(factory.createInvoke(className(c), "<init>", org.apache.bcel.generic.Type.VOID, wrapTypes(ctor.getParameterTypes()), Const.INVOKESPECIAL));
      return this;
    }

    public MethodMaker dup() {
      il.append(InstructionConst.DUP);
      return this;
    }

    public MethodMaker astore(int var) {
      il.append(new ASTORE(var));
      return this;
    }

    public int argIdx(int iArg) {
      return argStackIndex[iArg];
    }

    public MethodMaker aloadArgWithAutoboxing(int iArg) {
      var type = argTypes[iArg];
      int stackIdx = argStackIndex[iArg];
      if (type == org.apache.bcel.generic.Type.BYTE) {
        il.append(new ILOAD(stackIdx));
        invokeStatic(Byte.class, Byte.class, "valueOf", byte.class);
        return this;
      }
      if (type == org.apache.bcel.generic.Type.BOOLEAN) {
        il.append(new ILOAD(stackIdx));
        invokeStatic(Boolean.class, Boolean.class, "valueOf", boolean.class);
        return this;
      }
      if (type == org.apache.bcel.generic.Type.CHAR) {
        il.append(new ILOAD(stackIdx));
        invokeStatic(Character.class, Character.class, "valueOf", char.class);
        return this;
      }
      if (type == org.apache.bcel.generic.Type.SHORT) {
        il.append(new ILOAD(stackIdx));
        invokeStatic(Short.class, Short.class, "valueOf", short.class);
        return this;
      }
      if (type == org.apache.bcel.generic.Type.INT) {
        il.append(new ILOAD(stackIdx));
        invokeStatic(Integer.class, Integer.class, "valueOf", int.class);
        return this;
      }
      if (type == org.apache.bcel.generic.Type.LONG) {
        il.append(new LLOAD(stackIdx));
        invokeStatic(Long.class, Long.class, "valueOf", long.class);
        return this;
      }
      if (type == org.apache.bcel.generic.Type.FLOAT) {
        il.append(new LLOAD(stackIdx));
        invokeStatic(Float.class, Float.class, "valueOf", float.class);
        return this;
      }
      if (type == org.apache.bcel.generic.Type.DOUBLE) {
        il.append(new DLOAD(stackIdx));
        invokeStatic(Double.class, Double.class, "valueOf", double.class);
        return this;
      }
      return aload(stackIdx);
    }

    public MethodMaker aload(int stackIdx) {
      il.append(new ALOAD(stackIdx));
      return this;
    }

    public MethodMaker stringConstant(String s) {
      il.append(new PUSH(cp, s));
      return this;
    }

    public MethodMaker classConstant(Class c) {
      if (classConstantWorkaround) {
        stringConstant(c.getName());
        invokeStatic(Class.class, Class.class, "forName", String.class);
      } else {
        var ldc = new LDC(classRef(c));
        assertEquals("classConstant", ldc.getValue(cp), wrapType(c));
        il.append(ldc);
      }
      return this;
    }

    final public MethodMaker intConst(int i) {
      return intConstant(i);
    }

    public MethodMaker intConstant(int i) {
      if (i >= -1 && i <= 5)
        return add(new ICONST(i));
      if (i == (byte) i)
        return add(new BIPUSH((byte) i));
      if (i == (short) i)
        return add(new SIPUSH((short) i));
      return add(new LDC(cp.addInteger(i)));
    }

    final public MethodMaker doubleConst(double d) {
      return doubleConstant(d);
    }

    public MethodMaker doubleConstant(double d) {
      return add(new LDC2_W(cp.addDouble(d)));
    }

    public MethodMaker boolConstant(boolean b) {
      return intConstant(b ? 1 : 0);
    }

    public MethodMaker invokeVirtual(Class c, Class returnType, String methodName, Class... argTypes) {
      Method m = findNonStaticMethod_precise_onTypes(c, methodName, argTypes);
      if (m == null)
        throw fail("Method not found: " + className(c) + "." + formatFunctionCall(methodName, argTypes) + " returning " + className(returnType));
      il.append(factory.createInvoke(className(c), methodName, wrapType(m.getReturnType()), wrapTypes(m.getParameterTypes()), Const.INVOKEVIRTUAL));
      return this;
    }

    public MethodMaker invokeInterface(Class c, Class returnType, String methodName, Class... argTypes) {
      Method m = mostApplicableMethod_onTypes(filter(nonDefaultInterfaceMethods(c), _m -> _m.getName().equals(methodName)), argTypes);
      if (m == null)
        throw fail("Method not found: " + className(c) + "." + formatFunctionCall(methodName, argTypes) + " returning " + className(returnType));
      il.append(factory.createInvoke(className(c), methodName, wrapType(m.getReturnType()), wrapTypes(m.getParameterTypes()), Const.INVOKEINTERFACE));
      return this;
    }

    public MethodMaker invokeStatic(Class c, Class returnType, String methodName, Class... argTypes) {
      Method m = findMethod_precise_onTypes(c, methodName, argTypes);
      if (m == null)
        throw fail("Method not found: " + className(c) + "." + formatFunctionCall(methodName, argTypes) + " returning " + className(returnType));
      il.append(factory.createInvoke(className(c), methodName, wrapType(m.getReturnType()), wrapTypes(m.getParameterTypes()), Const.INVOKESTATIC));
      return this;
    }

    public MethodMaker areturn() {
      il.append(InstructionConst.ARETURN);
      return this;
    }

    public MethodMaker _return() {
      il.append(InstructionConst.RETURN);
      return this;
    }

    public MethodMaker returnPrimitive(Class type) {
      il.append(primitiveReturnInstruction(type));
      return this;
    }

    public ReturnInstruction primitiveReturnInstruction(Class type) {
      assertTrue(isPrimitiveType(type));
      if (type == long.class)
        return InstructionConst.LRETURN;
      if (type == float.class)
        return InstructionConst.FRETURN;
      if (type == double.class)
        return InstructionConst.DRETURN;
      return InstructionConst.IRETURN;
    }

    public MethodMaker add(Instruction i) {
      il.append(i);
      if (verboseAdd)
        print("> " + i);
      return this;
    }

    public <A extends Instruction> A addAndReturn(A i) {
      add(i);
      return i;
    }

    public MethodMaker add(BranchInstruction i) {
      il.append(i);
      if (verboseAdd)
        print("> " + i);
      return this;
    }

    public <A extends BranchInstruction> A addAndReturn(A i) {
      add(i);
      return i;
    }

    public void done() {
      mg.stripAttributes(true);
      mg.setMaxStack();
      mg.setMaxLocals();
      cg.addMethod(mg.getMethod());
    }

    public JVMStackCellType convertToObject(JVMStackCellType stackTop) {
      if (stackTop == JVMStackCellType.objValue) {
      } else if (stackTop == JVMStackCellType.intValue)
        invokeStatic(Integer.class, Integer.class, "valueOf", int.class);
      else if (stackTop == JVMStackCellType.doubleValue)
        invokeStatic(Double.class, Double.class, "valueOf", double.class);
      else if (stackTop == JVMStackCellType.none)
        add(new ACONST_NULL());
      else
        throw fail("TODO: add conversion for stack cell type: " + stackTop);
      return JVMStackCellType.objValue;
    }

    public void discardStackTop(JVMStackCellType stackTop) {
      if (stackTop == JVMStackCellType.none) {
      } else if (stackTop == JVMStackCellType.doubleValue || stackTop == JVMStackCellType.longValue)
        add(new POP2());
      else
        add(new POP());
    }

    public int classRef(Object c) {
      return cp.addClass((ObjectType) wrapType(assertNotNull(c)));
    }

    public MethodMaker checkCast(Object c) {
      return add(new CHECKCAST(classRef(c)));
    }

    public MethodMaker loadNull() {
      return add(new ACONST_NULL());
    }

    public InstructionHandle here() {
      return il.append(new NOP());
    }

    public GOTO forwardGoto() {
      return addAndReturn(new GOTO(null));
    }

    public void returnWithType(JVMStackCellType stackTop) {
      if (stackTop == JVMStackCellType.objValue)
        areturn();
      else if (stackTop == JVMStackCellType.intValue)
        add(new IRETURN());
      else if (stackTop == JVMStackCellType.doubleValue)
        add(new DRETURN());
      else if (stackTop == JVMStackCellType.none)
        _return();
      else
        throw fail("TODO: add return for stack cell type: " + stackTop);
    }

    public void getStaticField(String className, String fieldName, Class type) {
      il.append(factory.createGetStatic(className, fieldName, wrapType(type)));
    }
  }

  static public class Meta implements IMeta {

    volatile public Object meta;

    public void _setMeta(Object meta) {
      this.meta = meta;
    }

    public Object _getMeta() {
      return meta;
    }

    final public boolean scaffolding() {
      return scaffoldingEnabled();
    }

    final public boolean scaffolded() {
      return scaffoldingEnabled();
    }

    public boolean scaffoldingEnabled() {
      return main.scaffoldingEnabled(this);
    }

    public boolean scaffoldingEnabled(Object o) {
      return main.scaffoldingEnabled(o);
    }

    public String toString_base() {
      return super.toString();
    }

    public String toString() {
      Object o = metaGet("toString", this);
      if (o instanceof String)
        return ((String) o);
      if (o instanceof IF1)
        return str(((IF1) o).get(this));
      return toString_base();
    }
  }

  public enum JVMStackCellType {

    none,
    objValue,
    intValue,
    longValue,
    floatValue,
    doubleValue
  }

  static public class LASToByteCode implements IFieldsToList {

    public MethodMaker m;

    public LASToByteCode() {
    }

    public LASToByteCode(MethodMaker m) {
      this.m = m;
    }

    public String toString() {
      return shortClassName_dropNumberPrefix(this) + "(" + m + ")";
    }

    public Object[] _fieldsToList() {
      return new Object[] { m };
    }

    public boolean callPing = true;

    final public LASToByteCode setIVarContext(int iVarContext) {
      return iVarContext(iVarContext);
    }

    public LASToByteCode iVarContext(int iVarContext) {
      this.iVarContext = iVarContext;
      return this;
    }

    final public int getIVarContext() {
      return iVarContext();
    }

    public int iVarContext() {
      return iVarContext;
    }

    public int iVarContext = -1;

    public GazelleV_LeftArrowScript.Script returnableScript;

    final public LASToByteCode setPostConversion(IF1<JVMStackCellType, JVMStackCellType> postConversion) {
      return postConversion(postConversion);
    }

    public LASToByteCode postConversion(IF1<JVMStackCellType, JVMStackCellType> postConversion) {
      this.postConversion = postConversion;
      return this;
    }

    final public IF1<JVMStackCellType, JVMStackCellType> getPostConversion() {
      return postConversion();
    }

    public IF1<JVMStackCellType, JVMStackCellType> postConversion() {
      return postConversion;
    }

    public IF1<JVMStackCellType, JVMStackCellType> postConversion;

    public JVMStackCellType compileScript(GazelleV_LeftArrowScript.Script script) {
      returnableScript = script;
      var stackTop = compile(script);
      if (postConversion != null)
        stackTop = postConversion.get(stackTop);
      return stackTop;
    }

    public void compileToObject(GazelleV_LeftArrowScript.Evaluable code) {
      m.convertToObject(compile(code));
    }

    public JVMStackCellType compile(GazelleV_LeftArrowScript.Evaluable code) {
      if (code instanceof GazelleV_LeftArrowScript.Const) {
        Object o = ((GazelleV_LeftArrowScript.Const) code).value;
        if (o == null) {
          m.add(new ACONST_NULL());
          return JVMStackCellType.objValue;
        } else if (o instanceof String) {
          m.stringConstant((String) o);
          return JVMStackCellType.objValue;
        } else if (o instanceof Integer) {
          m.intConstant((Integer) o);
          return JVMStackCellType.intValue;
        } else if (o instanceof Double) {
          m.doubleConstant((Double) o);
          return JVMStackCellType.doubleValue;
        } else if (o instanceof Class) {
          m.classConstant((Class) o);
          return JVMStackCellType.objValue;
        } else if (o instanceof Boolean) {
          m.boolConstant((Boolean) o);
          return JVMStackCellType.intValue;
        } else
          throw fail("Can't compile const value: " + toStringWithClass(o));
      } else if (code instanceof GazelleV_LeftArrowScript.Script) {
        var stackTop = JVMStackCellType.none;
        for (var step : ((GazelleV_LeftArrowScript.Script) code).steps) {
          if (stackTop != JVMStackCellType.none)
            m.add(new POP());
          stackTop = compile(step);
        }
        return stackTop;
      } else if (code instanceof GazelleV_LeftArrowScript.CallMethod) {
        compileToObject(((GazelleV_LeftArrowScript.CallMethod) code).target);
        m.stringConstant(((GazelleV_LeftArrowScript.CallMethod) code).methodName);
        argumentsAsArray(((GazelleV_LeftArrowScript.CallMethod) code).args);
        m.invokeStatic(main.class, Object.class, "call", Object.class, String.class, Object[].class);
        return JVMStackCellType.objValue;
      } else if (code instanceof GazelleV_LeftArrowScript.CallMethodOrGetField) {
        compileToObject(((GazelleV_LeftArrowScript.CallMethodOrGetField) code).target);
        m.stringConstant(((GazelleV_LeftArrowScript.CallMethodOrGetField) code).name);
        m.invokeStatic(main.class, Object.class, "preciseGetOrCallMethod", Object.class, String.class);
        return JVMStackCellType.objValue;
      } else if (code instanceof GazelleV_LeftArrowScript.SetField) {
        compileToObject(((GazelleV_LeftArrowScript.SetField) code).target);
        m.stringConstant(((GazelleV_LeftArrowScript.SetField) code).name);
        compileToObject(((GazelleV_LeftArrowScript.SetField) code).expr);
        m.invokeStatic(main.class, Object.class, "set", Object.class, String.class, Object.class);
        return JVMStackCellType.none;
      } else if (code instanceof GazelleV_LeftArrowScript.NewObject) {
        m.classConstant(((GazelleV_LeftArrowScript.NewObject) code).c);
        argumentsAsArray(((GazelleV_LeftArrowScript.NewObject) code).args);
        m.invokeStatic(main.class, Object.class, "nuObject", Class.class, Object[].class);
        return JVMStackCellType.objValue;
      } else if (code instanceof GazelleV_LeftArrowScript.Assignment) {
        compileToObject(((GazelleV_LeftArrowScript.Assignment) code).expression);
        m.astore(iTemp());
        loadVarContext();
        m.stringConstant(((GazelleV_LeftArrowScript.Assignment) code).var);
        m.aload(iTemp());
        m.invokeVirtual(VarContext.class, void.class, "set", String.class, Object.class);
        m.aload(iTemp());
        return JVMStackCellType.objValue;
      } else if (code instanceof GazelleV_LeftArrowScript.While) {
        var loopStart = m.il.append(new NOP());
        BranchInstruction branch1 = null;
        if (callPing) {
          m.invokeStatic(main.class, boolean.class, "ping");
          branch1 = new IFEQ(null);
          m.add(branch1);
        }
        compileToBool(((GazelleV_LeftArrowScript.While) code).condition);
        var branch2 = new IFEQ(null);
        m.add(branch2);
        m.discardStackTop(compile(((GazelleV_LeftArrowScript.While) code).body));
        m.add(new GOTO(loopStart));
        var loopEnd = m.il.append(new NOP());
        {
          if (branch1 != null)
            branch1.setTarget(loopEnd);
        }
        branch2.setTarget(loopEnd);
        return JVMStackCellType.none;
      } else if (code instanceof GazelleV_LeftArrowScript.IfThen) {
        compileToBool(((GazelleV_LeftArrowScript.IfThen) code).condition);
        var branch1 = new IFEQ(null);
        m.add(branch1);
        var stackTop = compile(((GazelleV_LeftArrowScript.IfThen) code).body);
        if (stackTop == JVMStackCellType.none) {
          branch1.setTarget(m.here());
          return stackTop;
        } else {
          m.convertToObject(stackTop);
          var jumpToEnd = m.forwardGoto();
          branch1.setTarget(m.here());
          m.loadNull();
          jumpToEnd.setTarget(m.here());
          return JVMStackCellType.objValue;
        }
      } else if (code instanceof GazelleV_LeftArrowScript.GetVar) {
        return compileGetVar((GazelleV_LeftArrowScript.GetVar) code);
      } else if (code instanceof GazelleV_LeftArrowScript.ReturnFromScript) {
        if (((GazelleV_LeftArrowScript.ReturnFromScript) code).script != returnableScript)
          throw fail("Can only return from current script");
        var stackTop = compile(((GazelleV_LeftArrowScript.ReturnFromScript) code).value);
        if (postConversion != null)
          stackTop = postConversion.get(stackTop);
        m.returnWithType(stackTop);
        return JVMStackCellType.none;
      }
      throw fail("Can't compile yet: " + className(code));
    }

    public void argumentsAsArray(GazelleV_LeftArrowScript.Evaluable[] args) {
      int n = l(args);
      m.intConst(n);
      m.add(new ANEWARRAY(m.classRef(Object.class)));
      for (int iArg = 0; iArg < n; iArg++) {
        m.dup();
        m.intConst(iArg);
        compileToObject(args[iArg]);
        m.add(new AASTORE());
      }
    }

    public void loadVarContext() {
      assertTrue("Need VarContext", iVarContext >= 0);
      m.aload(iVarContext);
    }

    public Integer iTemp_cache;

    public int iTemp() {
      if (iTemp_cache == null)
        iTemp_cache = iTemp_load();
      return iTemp_cache;
    }

    public int iTemp_load() {
      return m.newLocalVar();
    }

    public void compileToBool(GazelleV_LeftArrowScript.Evaluable condition) {
      var stackTop = compile(condition);
      if (stackTop == JVMStackCellType.objValue) {
        m.checkCast(Boolean.class);
        m.invokeVirtual(Boolean.class, boolean.class, "booleanValue");
      } else if (stackTop == JVMStackCellType.intValue) {
      } else
        throw fail("Can't convert to bool: " + stackTop);
    }

    public JVMStackCellType compileGetVar(GazelleV_LeftArrowScript.GetVar code) {
      loadVarContext();
      m.stringConstant(code.var);
      m.invokeVirtual(VarContext.class, Object.class, "get", String.class);
      return JVMStackCellType.objValue;
    }
  }

  static public interface IAutoCloseableF0<A> extends IF0<A>, AutoCloseable {
  }

  public interface IResolvableClass extends Type {

    public String resolveToClassName();

    public Class resolveToClass();
  }

  static public class LineAndColumn implements IFieldsToList {

    static final public String _fieldOrder = "line col getLineText";

    public int line;

    public int col;

    public LineAndColumn() {
    }

    public LineAndColumn(int line, int col) {
      this.col = col;
      this.line = line;
    }

    public boolean equals(Object o) {
      if (!(o instanceof LineAndColumn))
        return false;
      LineAndColumn __1 = (LineAndColumn) o;
      return line == __1.line && col == __1.col;
    }

    public int hashCode() {
      int h = -1128952231;
      h = boostHashCombine(h, _hashCode(line));
      h = boostHashCombine(h, _hashCode(col));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { line, col };
    }

    transient public IF1<Integer, String> getLineText;

    public String getLineText(int line) {
      return getLineText != null ? getLineText.get(line) : getLineText_base(line);
    }

    final public String getLineText_fallback(IF1<Integer, String> _f, int line) {
      return _f != null ? _f.get(line) : getLineText_base(line);
    }

    public String getLineText_base(int line) {
      return null;
    }

    public String toString() {
      return "Line " + n2(line) + ", col " + n2(col);
    }
  }

  static public class ClassMaker<A> {

    final public String getClassName() {
      return className();
    }

    public String className() {
      return className;
    }

    public String className;

    final public ClassGen getCg() {
      return cg();
    }

    public ClassGen cg() {
      return cg;
    }

    public ClassGen cg;

    public JavaClass baked;

    public InMemoryClassLoader classLoader;

    public Class loadedClass;

    final public ClassMaker<A> setPrintDisassembly(boolean printDisassembly) {
      return printDisassembly(printDisassembly);
    }

    public ClassMaker<A> printDisassembly(boolean printDisassembly) {
      this.printDisassembly = printDisassembly;
      return this;
    }

    final public boolean getPrintDisassembly() {
      return printDisassembly();
    }

    public boolean printDisassembly() {
      return printDisassembly;
    }

    public boolean printDisassembly = false;

    final public ClassMaker<A> setSuperClassName(String superClassName) {
      return superClassName(superClassName);
    }

    public ClassMaker<A> superClassName(String superClassName) {
      this.superClassName = superClassName;
      return this;
    }

    final public String getSuperClassName() {
      return superClassName();
    }

    public String superClassName() {
      return superClassName;
    }

    public String superClassName = "java.lang.Object";

    final public ClassMaker<A> setFileName(String fileName) {
      return fileName(fileName);
    }

    public ClassMaker<A> fileName(String fileName) {
      this.fileName = fileName;
      return this;
    }

    final public String getFileName() {
      return fileName();
    }

    public String fileName() {
      return fileName;
    }

    public String fileName;

    public ClassMaker(String className) {
      this(className, null);
    }

    public ClassMaker(String className, String superClassName, String[] interfaces) {
      this.superClassName = superClassName;
      this.className = className;
      setClassGen(new ClassGen(className, superClassName, fileName, Const.ACC_PUBLIC, interfaces));
    }

    public ClassMaker(String className, String[] interfaces) {
      this.className = className;
      setClassGen(new ClassGen(className, superClassName, fileName, Const.ACC_PUBLIC, interfaces));
    }

    public ClassMaker(Class<A> interfaceToImplement) {
      className = randomClassName();
      setClassGen(new ClassGen(className, "java.lang.Object", null, Const.ACC_PUBLIC, new String[] { main.className(interfaceToImplement) }));
      addDefaultConstructor();
    }

    public void addDefaultConstructor() {
      cg.addEmptyConstructor(Const.ACC_PUBLIC);
    }

    public void setClassGen(ClassGen cg) {
      this.cg = cg;
      cg.setMajor(50);
      cg.setMinor(0);
    }

    public JavaClass bake() {
      if (baked == null) {
        baked = cg.getJavaClass();
        if (printDisassembly)
          printClassWithMethods();
      }
      return baked;
    }

    public void printClassWithMethods() {
      print_tabToSingleSpace(bake());
      for (var method : baked.getMethods()) {
        print_tabToSingleSpace("\n" + method);
        print_tabToSingleSpace(method.getCode());
      }
    }

    public byte[] getBytes_cache;

    final public byte[] getBytes() {
      if (getBytes_cache == null)
        getBytes_cache = getBytes_load();
      return getBytes_cache;
    }

    final public byte[] getBytes_load() {
      return toBytes();
    }

    public byte[] toBytes() {
      return bake().getBytes();
    }

    public Class<A> load() {
      if (loadedClass == null) {
        var bytes = toBytes();
        classLoader = new InMemoryClassLoader(myClassLoader());
        loadedClass = (Class) classLoader.defineAClass(className, bytes);
      }
      return loadedClass;
    }

    public A newInstance() {
      return main.newInstance(load());
    }

    public void addField(FieldGen fg) {
      cg.addField(fg.getField());
    }

    public ConstantPoolGen getConstantPool() {
      return cg.getConstantPool();
    }
  }

  abstract static public class Sleeping implements AutoCloseable, IFieldsToList {

    public Timestamp targetTime;

    public Runnable action;

    public Sleeping() {
    }

    public Sleeping(Timestamp targetTime, Runnable action) {
      this.action = action;
      this.targetTime = targetTime;
    }

    public String toString() {
      return shortClassName_dropNumberPrefix(this) + "(" + targetTime + ", " + action + ")";
    }

    public Object[] _fieldsToList() {
      return new Object[] { targetTime, action };
    }

    public long remainingMS() {
      return targetTime.minus(tsNow());
    }
  }

  static public class AppendableChain<A> extends MinimalChain<A> implements Iterable<A>, IntSize {

    public MinimalChain<A> last;

    final public int getSize() {
      return size();
    }

    public int size() {
      return size;
    }

    public int size;

    public AppendableChain() {
    }

    public AppendableChain(A element) {
      this.element = element;
      size = 1;
      last = this;
    }

    public AppendableChain(A element, AppendableChain<A> next) {
      this.next = next;
      this.element = element;
      if (next == null)
        return;
      MinimalChain<A> b = new MinimalChain();
      b.element = next.element;
      b.next = next.next;
      this.next = b;
      last = next.last;
      size = next.size + 1;
    }

    public String toString() {
      return str(toList());
    }

    public boolean add(A a) {
      MinimalChain newLast = new MinimalChain(a);
      last.next = newLast;
      last = newLast;
      ++size;
      return true;
    }

    public AppendableChain<A> popFirst() {
      if (next == null)
        return null;
      element = next.element;
      if (last == next)
        last = this;
      next = next.next;
      --size;
      return this;
    }

    public ArrayList<A> toList() {
      ArrayList<A> l = emptyList(size);
      MinimalChain<A> c = this;
      while (c != null) {
        l.add(c.element);
        c = c.next;
      }
      return l;
    }

    public class ACIt extends IterableIterator<A> {

      public MinimalChain<A> c = AppendableChain.this;

      public boolean hasNext() {
        return c != null;
      }

      public A next() {
        var a = c.element;
        c = c.next;
        return a;
      }
    }

    public IterableIterator<A> iterator() {
      return new ACIt();
    }
  }

  static public class MultiSleeper implements ISleeper_v2, AutoCloseable {

    public MultiSetMap<Timestamp, Runnable> entries = treeMultiSetMap();

    public RestartableCountdown countdown = new RestartableCountdown();

    private void check() {
      var time = nextWakeUpTime();
      var action = firstValue(entries);
      countdown.setTargetTime(time == null ? 0 : time.sysTime(), new Runnable() {

        public void run() {
          try {
            Set<Runnable> toCall;
            synchronized (MultiSleeper.this) {
              toCall = entries.get(time);
              entries.remove(time);
              check();
            }
            pcallFAll(toCall);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "Set<Runnable> toCall;\r\n      synchronized(MultiSleeper.this) {\r\n        toCal...";
        }
      });
    }

    private synchronized void removeEntry(Timestamp targetTime, Runnable action) {
      entries.remove(targetTime, action);
    }

    synchronized public Timestamp nextWakeUpTime() {
      return firstKey(entries);
    }

    public synchronized Sleeping doLater(Timestamp targetTime, Runnable r) {
      if (r == null || targetTime == null)
        return null;
      targetTime = max(targetTime, tsNow());
      entries.put(targetTime, r);
      check();
      return new Sleeping(targetTime, r) {

        public void close() {
          try {
            removeEntry(targetTime, r);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }
      };
    }

    public void close() {
      try {
        {
          cleanUp(countdown);
          countdown = null;
        }
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }
  }

  static public class ListAndIndex<A> implements IFieldsToList {

    static final public String _fieldOrder = "list idx";

    public List<A> list;

    public int idx;

    public ListAndIndex() {
    }

    public ListAndIndex(List<A> list, int idx) {
      this.idx = idx;
      this.list = list;
    }

    public boolean equals(Object o) {
      if (!(o instanceof ListAndIndex))
        return false;
      ListAndIndex __1 = (ListAndIndex) o;
      return eq(list, __1.list) && idx == __1.idx;
    }

    public int hashCode() {
      int h = 276903961;
      h = boostHashCombine(h, _hashCode(list));
      h = boostHashCombine(h, _hashCode(idx));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { list, idx };
    }

    public boolean atEnd() {
      return idx >= l(list);
    }

    public A get() {
      return _get(list, idx);
    }

    public int size() {
      return l(list);
    }

    public String toString() {
      return subList(list, 0, idx) + ", then " + subList(list, idx);
    }

    public ListAndIndex<A> plus(int ofs) {
      return new ListAndIndex(list, idx + ofs);
    }

    public ListAndIndex<A> minus(int ofs) {
      return new ListAndIndex(list, idx - ofs);
    }

    public List<A> list() {
      return list;
    }

    final public int idx() {
      return index();
    }

    public int index() {
      return idx;
    }

    public ListAndIndex<A> mapIdx(IF1_IntToInt f) {
      return new ListAndIndex<A>(list, f.get(idx));
    }
  }

  static public class TokenRange extends IntRange {

    public TokenRange() {
    }

    public TokenRange(int start, int end) {
      this.end = end;
      this.start = start;
    }
  }

  static public class VarMatches extends WrappedMap<String, String> {

    public VarMatches() {
      super(new LinkedHashMap());
    }

    public VarMatches(Map<String, String> map) {
      this();
      main.putAll(this, map);
    }
  }

  static public class WrappedMap<A, B> extends AbstractMap<A, B> {

    public Map<A, B> map;

    public WrappedMap() {
    }

    public WrappedMap(Map<A, B> map) {
      this.map = map;
    }

    public B get(Object key) {
      return map.get(key);
    }

    public B put(A key, B value) {
      return map.put(key, value);
    }

    public boolean containsKey(Object key) {
      return map.containsKey(key);
    }

    public Set<Map.Entry<A, B>> entrySet() {
      return map.entrySet();
    }
  }

  static public class MultiSetMap<A, B> implements IMultiMap<A, B> {

    public Map<A, Set<B>> data = new HashMap<A, Set<B>>();

    public int size;

    public MultiSetMap() {
    }

    public MultiSetMap(boolean useTreeMap) {
      if (useTreeMap)
        data = new TreeMap();
    }

    public MultiSetMap(MultiSetMap<A, B> map) {
      putAll(map);
    }

    public MultiSetMap(Map<A, Set<B>> data) {
      this.data = data;
    }

    public boolean put(A key, B value) {
      synchronized (data) {
        Set<B> set = data.get(key);
        if (set == null)
          data.put(key, set = _makeEmptySet());
        if (!set.add(value))
          return false;
        {
          ++size;
          return true;
        }
      }
    }

    public boolean add(A key, B value) {
      return put(key, value);
    }

    public void addAll(A key, Collection<B> values) {
      synchronized (data) {
        putAll(key, values);
      }
    }

    public void addAllIfNotThere(A key, Collection<B> values) {
      synchronized (data) {
        for (B value : values) setPut(key, value);
      }
    }

    public void setPut(A key, B value) {
      synchronized (data) {
        if (!containsPair(key, value))
          put(key, value);
      }
    }

    final public boolean contains(A key, B value) {
      return containsPair(key, value);
    }

    public boolean containsPair(A key, B value) {
      synchronized (data) {
        return get(key).contains(value);
      }
    }

    public void putAll(A key, Collection<B> values) {
      synchronized (data) {
        for (B value : values) put(key, value);
      }
    }

    public void removeAll(A key, Collection<B> values) {
      synchronized (data) {
        for (B value : values) remove(key, value);
      }
    }

    public Set<B> get(A key) {
      synchronized (data) {
        Set<B> set = data.get(key);
        return set == null ? Collections.<B>emptySet() : set;
      }
    }

    public List<B> getAndClear(A key) {
      synchronized (data) {
        List<B> l = cloneList(data.get(key));
        remove(key);
        return l;
      }
    }

    public Set<B> getOpt(A key) {
      synchronized (data) {
        return data.get(key);
      }
    }

    public Set<B> getActual(A key) {
      synchronized (data) {
        Set<B> set = data.get(key);
        if (set == null)
          data.put(key, set = _makeEmptySet());
        return set;
      }
    }

    public void clean(A key) {
      synchronized (data) {
        Set<B> list = data.get(key);
        if (list != null && list.isEmpty())
          data.remove(key);
      }
    }

    final public Set<A> keys() {
      return keySet();
    }

    public Set<A> keySet() {
      synchronized (data) {
        return data.keySet();
      }
    }

    public void remove(A key) {
      synchronized (data) {
        size -= l(data.get(key));
        data.remove(key);
      }
    }

    public void remove(A key, B value) {
      synchronized (data) {
        Set<B> set = data.get(key);
        if (set != null) {
          if (set.remove(value)) {
            --size;
            if (set.isEmpty())
              data.remove(key);
          }
        }
      }
    }

    public void clear() {
      synchronized (data) {
        data.clear();
        size = 0;
      }
    }

    public boolean containsKey(A key) {
      synchronized (data) {
        return data.containsKey(key);
      }
    }

    public B getFirst(A key) {
      synchronized (data) {
        return first(get(key));
      }
    }

    public void addAll(MultiSetMap<A, B> map) {
      putAll(map);
    }

    public void putAll(MultiSetMap<A, B> map) {
      synchronized (data) {
        for (A key : map.keySet()) putAll(key, map.get(key));
      }
    }

    public void putAll(Map<A, B> map) {
      synchronized (data) {
        if (map != null)
          for (Map.Entry<A, B> e : map.entrySet()) put(e.getKey(), e.getValue());
      }
    }

    final public int keyCount() {
      return keysSize();
    }

    public int keysSize() {
      synchronized (data) {
        return l(data);
      }
    }

    public int size() {
      synchronized (data) {
        return size;
      }
    }

    public int getSize(A key) {
      return l(data.get(key));
    }

    public int count(A key) {
      return getSize(key);
    }

    public Set<A> reverseGet(B b) {
      synchronized (data) {
        Set<A> l = new HashSet();
        for (A key : data.keySet()) if (data.get(key).contains(b))
          l.add(key);
        return l;
      }
    }

    public A keyForValue(B b) {
      synchronized (data) {
        for (A key : data.keySet()) if (data.get(key).contains(b))
          return key;
        return null;
      }
    }

    public Map<A, Set<B>> asMap() {
      synchronized (data) {
        return cloneMap(data);
      }
    }

    public boolean isEmpty() {
      synchronized (data) {
        return data.isEmpty();
      }
    }

    public Set<B> _makeEmptySet() {
      return new HashSet();
    }

    public Collection<Set<B>> allLists() {
      synchronized (data) {
        return new HashSet(data.values());
      }
    }

    public List<B> allValues() {
      return concatLists(values(data));
    }

    public List<Pair<A, B>> allEntries() {
      synchronized (data) {
        List<Pair<A, B>> l = emptyList(size);
        for (Map.Entry<? extends A, ? extends Set<B>> __0 : _entrySet(data)) {
          A a = __0.getKey();
          Set<B> set = __0.getValue();
          for (B b : set) l.add(pair(a, b));
        }
        return l;
      }
    }

    public Object mutex() {
      return data;
    }

    public String toString() {
      return "mm" + str(data);
    }

    public Pair<A, B> firstEntry() {
      synchronized (data) {
        if (empty(data))
          return null;
        Map.Entry<A, Set<B>> entry = data.entrySet().iterator().next();
        return pair(entry.getKey(), first(entry.getValue()));
      }
    }

    public A firstKey() {
      synchronized (data) {
        return main.firstKey(data);
      }
    }

    public A lastKey() {
      synchronized (data) {
        return (A) ((NavigableMap) data).lastKey();
      }
    }

    public A higherKey(Object a) {
      synchronized (data) {
        return (A) ((NavigableMap) data).higherKey(a);
      }
    }
  }

  static public class MinimalChain<A> implements Iterable<A> {

    public A element;

    public MinimalChain<A> next;

    public MinimalChain() {
    }

    public MinimalChain(A element) {
      this.element = element;
    }

    public MinimalChain(A element, MinimalChain<A> next) {
      this.next = next;
      this.element = element;
    }

    public String toString() {
      return str(toList());
    }

    public ArrayList<A> toList() {
      ArrayList<A> l = new ArrayList();
      MinimalChain<A> c = this;
      while (c != null) {
        l.add(c.element);
        c = c.next;
      }
      return l;
    }

    public void setElement(A a) {
      element = a;
    }

    public void setNext(MinimalChain<A> next) {
      this.next = next;
    }

    public Iterator<A> iterator() {
      return toList().iterator();
    }

    public A get() {
      return element;
    }
  }

  static public class InMemoryClassLoader extends ClassLoader {

    final public InMemoryClassLoader setRememberClassBytes(boolean rememberClassBytes) {
      return rememberClassBytes(rememberClassBytes);
    }

    public InMemoryClassLoader rememberClassBytes(boolean rememberClassBytes) {
      this.rememberClassBytes = rememberClassBytes;
      return this;
    }

    final public boolean getRememberClassBytes() {
      return rememberClassBytes();
    }

    public boolean rememberClassBytes() {
      return rememberClassBytes;
    }

    transient public boolean rememberClassBytes = false;

    transient public Map<Class, byte[]> classBytes = syncMap();

    public InMemoryClassLoader(ClassLoader parent) {
      super(parent);
    }

    public Class<?> defineAClass(String name, byte[] bytes) {
      Class c = defineClass(name, bytes, 0, bytes.length);
      if (rememberClassBytes)
        classBytes.put(c, bytes);
      return c;
    }

    public byte[] getClassBytes(Class c) {
      return classBytes.get(c);
    }
  }

  static public class IntRange {

    public int start, end;

    public IntRange() {
    }

    public IntRange(int start, int end) {
      this.end = end;
      this.start = start;
    }

    public IntRange(IntRange r) {
      start = r.start;
      end = r.end;
    }

    public boolean equals(Object o) {
      return stdEq2(this, o);
    }

    public int hashCode() {
      return stdHash2(this);
    }

    final public int length() {
      return end - start;
    }

    final public boolean empty() {
      return start >= end;
    }

    final public boolean isEmpty() {
      return start >= end;
    }

    static public String _fieldOrder = "start end";

    public String toString() {
      return "[" + start + ";" + end + "]";
    }

    public boolean contains(int i) {
      return i >= start && i <= end;
    }
  }

  static public class RestartableCountdown implements AutoCloseable {

    public java.util.Timer timer;

    public long targetTime;

    public long totalSleepTime;

    synchronized public void setTargetTime(long targetTime, Runnable action) {
      if (targetTime <= 0)
        stop();
      else if (targetTime != this.targetTime) {
        start(targetTime - sysNow(), action);
        this.targetTime = targetTime;
      }
    }

    synchronized public void start(long delayMS, Object action) {
      stop();
      if (delayMS <= 0) {
        startThread(new Runnable() {

          public void run() {
            try {
              callF(action);
            } catch (Exception __e) {
              throw rethrow(__e);
            }
          }

          public String toString() {
            return "callF(action);";
          }
        });
      } else {
        totalSleepTime += delayMS;
        timer = doLater_daemon(delayMS, action);
        targetTime = sysNow() + delayMS;
      }
    }

    public void start(double delaySeconds, Object action) {
      start(toMS(delaySeconds), action);
    }

    synchronized public void stop() {
      cancelTimer(timer);
      timer = null;
      targetTime = 0;
    }

    public void close() {
      stop();
    }
  }

  static public interface IF1_IntToInt {

    public int get(int i);
  }

  static public Object preciseGetOrCallMethod(Object object, String name) {
    if (object == null)
      return null;
    if (canCallWithVarargs(object, name))
      return call(object, name);
    return _get(object, name);
  }

  static public <A> A set(A o, String field, Object value) {
    if (o == null)
      return null;
    if (o instanceof Class)
      set((Class) o, field, value);
    else
      try {
        Field f = set_findField(o.getClass(), field);
        makeAccessible(f);
        smartSet(f, o, value);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    return o;
  }

  static public void set(Class c, String field, Object value) {
    if (c == null)
      return;
    try {
      Field f = set_findStaticField(c, field);
      makeAccessible(f);
      smartSet(f, null, value);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  static public Field set_findStaticField(Class<?> c, String field) {
    Class _c = c;
    do {
      for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0)
        return f;
      _c = _c.getSuperclass();
    } while (_c != null);
    throw new RuntimeException("Static field '" + field + "' not found in " + c.getName());
  }

  static public Field set_findField(Class<?> c, String field) {
    Class _c = c;
    do {
      for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field))
        return f;
      _c = _c.getSuperclass();
    } while (_c != null);
    throw new RuntimeException("Field '" + field + "' not found in " + c.getName());
  }

  static public void set(BitSet bs, int idx) {
    {
      if (bs != null)
        bs.set(idx);
    }
  }

  static public Class<?> getClass(String name) {
    return _getClass(name);
  }

  static public Class getClass(Object o) {
    return _getClass(o);
  }

  static public Class getClass(Object realm, String name) {
    return _getClass(realm, name);
  }

  static public boolean classIsExportedTo(Class c, java.lang.Module destModule) {
    if (c == null || destModule == null)
      return false;
    java.lang.Module srcModule = c.getModule();
    String packageName = c.getPackageName();
    return srcModule.isExported(packageName, destModule);
  }

  static public boolean isAbstract(Class c) {
    return (c.getModifiers() & Modifier.ABSTRACT) != 0;
  }

  static public boolean isAbstract(Method m) {
    return (m.getModifiers() & Modifier.ABSTRACT) != 0;
  }

  static public boolean reflection_isForbiddenMethod(Method m) {
    return m.getDeclaringClass() == Object.class && eqOneOf(m.getName(), "finalize", "clone", "registerNatives");
  }

  static public Set<Class> allInterfacesImplementedBy(Object o) {
    return allInterfacesImplementedBy(_getClass(o));
  }

  static public Set<Class> allInterfacesImplementedBy(Class c) {
    if (c == null)
      return null;
    HashSet<Class> set = new HashSet();
    allInterfacesImplementedBy_find(c, set);
    return set;
  }

  static public void allInterfacesImplementedBy_find(Class c, Set<Class> set) {
    if (c.isInterface() && !set.add(c))
      return;
    do {
      for (Class intf : c.getInterfaces()) allInterfacesImplementedBy_find(intf, set);
    } while ((c = c.getSuperclass()) != null);
  }

  static public Method findStaticMethod(Class c, String method, Object... args) {
    Class _c = c;
    while (c != null) {
      for (Method m : c.getDeclaredMethods()) {
        if (!m.getName().equals(method))
          continue;
        if ((m.getModifiers() & Modifier.STATIC) == 0 || !findStaticMethod_checkArgs(m, args))
          continue;
        return m;
      }
      c = c.getSuperclass();
    }
    return null;
  }

  static public boolean findStaticMethod_checkArgs(Method m, Object[] args) {
    Class<?>[] types = m.getParameterTypes();
    if (types.length != args.length)
      return false;
    for (int i = 0; i < types.length; i++) if (!(args[i] == null || isInstanceX(types[i], args[i])))
      return false;
    return true;
  }

  static public String unquote(String s) {
    if (s == null)
      return null;
    if (startsWith(s, '[')) {
      int i = 1;
      while (i < s.length() && s.charAt(i) == '=') ++i;
      if (i < s.length() && s.charAt(i) == '[') {
        String m = s.substring(1, i);
        if (s.endsWith("]" + m + "]"))
          return s.substring(i + 1, s.length() - i - 1);
      }
    }
    return unquoteSingleOrDoubleQuotes(s);
  }

  static public List<String> quoteAll(String[] l) {
    return quoteAll(asList(l));
  }

  static public List<String> quoteAll(Collection<String> l) {
    List<String> x = new ArrayList();
    for (String s : l) x.add(quote(s));
    return x;
  }

  static public int _hashCode(Object a) {
    return a == null ? 0 : a.hashCode();
  }

  static public boolean arraysEqual(Object[] a, Object[] b) {
    if (a.length != b.length)
      return false;
    for (int i = 0; i < a.length; i++) if (neq(a[i], b[i]))
      return false;
    return true;
  }

  static public float clamp(float x, float a, float b) {
    return x < a ? a : x > b ? b : x;
  }

  static public double clamp(double x, double a, double b) {
    return x < a ? a : x > b ? b : x;
  }

  static public int clamp(int x, int a, int b) {
    return x < a ? a : x > b ? b : x;
  }

  static public long clamp(long x, long a, long b) {
    return x < a ? a : x > b ? b : x;
  }

  static public void add(BitSet bs, int i) {
    bs.set(i);
  }

  static public <A> boolean add(Collection<A> c, A a) {
    return c != null && c.add(a);
  }

  static public void add(Container c, Component x) {
    addToContainer(c, x);
  }

  static public long add(AtomicLong l, long b) {
    return l.addAndGet(b);
  }

  static public <A> A _print(String s, A a) {
    return print(s, a);
  }

  static public <A> A _print(A a) {
    return print(a);
  }

  static public void _print() {
    print();
  }

  static public boolean _eq(Object a, Object b) {
    return eq(a, b);
  }

  static public String toString(Object o) {
    return strOrNull(o);
  }

  static public String formatDouble(double d, int digits) {
    String format = digits <= 0 ? "0" : "0." + rep(digits, '#');
    return decimalFormatEnglish(format, d);
  }

  static public String formatDouble(double d) {
    return str(d);
  }

  static public double ratioToPercent(double x, double y) {
    return x * 100 / y;
  }

  static public <A, B extends A> void addAll(Collection<A> c, Iterable<B> b) {
    if (c != null && b != null)
      for (A a : b) c.add(a);
  }

  static public <A, B extends A> boolean addAll(Collection<A> c, Collection<B> b) {
    return c != null && b != null && c.addAll(b);
  }

  static public <A, B extends A> boolean addAll(Collection<A> c, B... b) {
    return c != null && b != null && c.addAll(Arrays.asList(b));
  }

  static public <A, B> Map<A, B> addAll(Map<A, B> a, Map<? extends A, ? extends B> b) {
    if (a != null && b != null)
      a.putAll(b);
    return a;
  }

  static public <A extends Container> A addAll(A c, Collection<? extends Component> components) {
    return addComponents(c, components);
  }

  static public <A extends Container> A addAll(A c, Component... components) {
    return addComponents(c, components);
  }

  static public int boostHashCombine(int a, int b) {
    return a ^ (b + 0x9e3779b9 + (a << 6) + (a >>> 2));
  }

  static public long now_virtualTime;

  static public long now() {
    return now_virtualTime != 0 ? now_virtualTime : System.currentTimeMillis();
  }

  static public String formatLocalDateWithSeconds(long time) {
    return localDateWithSeconds(time);
  }

  static public String formatLocalDateWithSeconds() {
    return localDateWithSeconds();
  }

  static public long toMS(double seconds) {
    return (long) (seconds * 1000);
  }

  static public long toMS(Duration d) {
    return d == null ? 0 : d.toMillis();
  }

  static public long clockTimeToSystemTime(long now) {
    return now == 0 ? 0 : now + clockToSysTimeDiff();
  }

  static public TreeSet<String> ciSet() {
    return caseInsensitiveSet();
  }

  static public <A> A last(List<A> l) {
    return empty(l) ? null : l.get(l.size() - 1);
  }

  static public char last(String s) {
    return empty(s) ? '#' : s.charAt(l(s) - 1);
  }

  static public byte last(byte[] a) {
    return l(a) != 0 ? a[l(a) - 1] : 0;
  }

  static public int last(int[] a) {
    return l(a) != 0 ? a[l(a) - 1] : 0;
  }

  static public long last(long[] a) {
    return l(a) != 0 ? a[l(a) - 1] : 0;
  }

  static public double last(double[] a) {
    return l(a) != 0 ? a[l(a) - 1] : 0;
  }

  static public <A> A last(A[] a) {
    return l(a) != 0 ? a[l(a) - 1] : null;
  }

  static public <A> A last(Iterator<A> it) {
    A a = null;
    while (it.hasNext()) {
      ping();
      a = it.next();
    }
    return a;
  }

  static public <A> A last(Collection<A> l) {
    if (l == null)
      return null;
    if (l instanceof List)
      return (A) last((List) l);
    if (l instanceof SortedSet)
      return (A) last((SortedSet) l);
    Iterator<A> it = iterator(l);
    A a = null;
    while (it.hasNext()) {
      ping();
      a = it.next();
    }
    return a;
  }

  static public <A> A last(SortedSet<A> l) {
    return l == null ? null : l.last();
  }

  static public <A> A last(ReverseChain<A> l) {
    return l == null ? null : l.element;
  }

  static public <A> A last(CompactLinkedHashSet<A> set) {
    return set == null ? null : set.last();
  }

  static public <A> void replaceLast(List<A> l, A a) {
    replaceLastElement(l, a);
  }

  static public boolean nempty(Collection c) {
    return !empty(c);
  }

  static public boolean nempty(CharSequence s) {
    return !empty(s);
  }

  static public boolean nempty(Object[] o) {
    return !empty(o);
  }

  static public boolean nempty(byte[] o) {
    return !empty(o);
  }

  static public boolean nempty(int[] o) {
    return !empty(o);
  }

  static public boolean nempty(BitSet bs) {
    return !empty(bs);
  }

  static public boolean nempty(Map m) {
    return !empty(m);
  }

  static public boolean nempty(Iterator i) {
    return i != null && i.hasNext();
  }

  static public boolean nempty(IMultiMap mm) {
    return mm != null && mm.size() != 0;
  }

  static public boolean nempty(Object o) {
    return !empty(o);
  }

  static public boolean nempty(IntRange r) {
    return !empty(r);
  }

  static public boolean nempty(Rect r) {
    return r != null && r.w != 0 && r.h != 0;
  }

  static public boolean nempty(MultiSet ms) {
    return ms != null && !ms.isEmpty();
  }

  static public boolean nempty(IntSize l) {
    return l != null && l.size() != 0;
  }

  static public <A> A[] toTypedArray(Class<A> type, Iterable<A> c) {
    return toArray(c, type);
  }

  static public <A> String pnlToLines(String prefix, Iterable<A> l) {
    return pnlToString(prefix, l);
  }

  static public <A> String pnlToLines(Iterable<A> l) {
    return pnlToString(l);
  }

  static public <A> String pnlToLines(A[] l) {
    return pnlToString(l);
  }

  static public <A, B> String pnlToLines(Map<A, B> map) {
    return pnlToString(map);
  }

  static public <A, B> String pnlToLines(MultiMap<A, B> map) {
    return pnlToString(map);
  }

  static public String formatRecordVars(String recordName, Object... params) {
    return renderRecordVars(recordName, params);
  }

  static public String indentx(Object s) {
    return indentx(strOrEmpty(s));
  }

  static public String indentx(String s) {
    return indentx(indent_default, s);
  }

  static public String indentx(int n, String s) {
    return dropSuffix(repeat(' ', n), indent(n, s));
  }

  static public String indentx(String indent, String s) {
    return dropSuffix(indent, indent(indent, s));
  }

  static public TokenIndexedList3 tokenIndexedList3(List<String> l) {
    return l instanceof TokenIndexedList3 ? ((TokenIndexedList3) l) : new TokenIndexedList3(l);
  }

  static public void tok_ifdef(List<String> tok, IF1<String, Boolean> isDefined) {
    jreplace_dyn(tok, "ifdef <id> or <id>", (_tok, cIdx) -> {
      String flag1 = tok.get(cIdx + 2), flag2 = tok.get(cIdx + 6);
      boolean value = isDefined.get(flag1) || isDefined.get(flag2);
      return "ifdef " + value;
    });
    tok_conditionals(tok, "ifdef", "endifdef", isDefined, true, false);
  }

  static public void tok_pcall_script(List<String> tok) {
    replaceKeywordBlock(tok, "pcall", "try {", "} catch __e { pcallFail __e }");
  }

  static public void tok_script_settable(List<String> tok) {
    int i;
    while ((i = jfind(tok, "settable <id> :")) >= 0) {
      String id = tok.get(i + 2);
      int iType = i + 6;
      String type = tok_script_scanType(tok, i + 6);
      String getter = "def !synthetic " + id + ": " + type + " { this !_getField_ " + id + "}";
      String setter = "def !synthetic " + id + " " + id + ": " + type + " { this " + id + " <- " + id + "; this }";
      replaceToken_reTok(tok, i, joinWithSpace(getter, setter));
    }
  }

  static public String jreplace(String s, String in, String out) {
    return jreplace(s, in, out, null);
  }

  static public String jreplace(String s, String in, String out, Object condition) {
    List<String> tok = javaTok(s);
    return jreplace(tok, in, out, condition) ? join(tok) : s;
  }

  static public boolean jreplace(List<String> tok, String in, String out) {
    return jreplace(tok, in, out, false, true, null);
  }

  static public boolean jreplace(List<String> tok, String in, String out, Object condition) {
    return jreplace(tok, in, out, false, true, condition);
  }

  static public boolean jreplace(List<String> tok, String in, ITokCondition condition, String out) {
    return jreplace(tok, in, out, (Object) condition);
  }

  static public boolean jreplace(List<String> tok, String in, String out, IF2<List<String>, Integer, Boolean> condition) {
    return jreplace(tok, in, out, (Object) condition);
  }

  static public boolean jreplace(List<String> tok, String in, String out, boolean ignoreCase, boolean reTok, Object condition) {
    String[] toks = javaTokForJFind_array(in);
    int lTokin = toks.length * 2 + 1;
    boolean anyChange = false;
    int i = -1;
    for (int n = 0; n < 10000; n++) {
      i = findCodeTokens(tok, i + 1, ignoreCase, toks, condition);
      if (i < 0)
        return anyChange;
      List<String> subList = tok.subList(i - 1, i + lTokin - 1);
      String expansion = jreplaceExpandRefs(out, subList);
      int end = i + lTokin - 2;
      clearAllTokens(tok, i, end);
      tok.set(i, expansion);
      if (reTok)
        reTok(tok, i, end);
      i = end;
      anyChange = true;
    }
    throw fail("woot? 10000! " + quote(in) + " => " + quote(out));
  }

  static public boolean jreplace_debug = false;

  static public boolean setText_opt = true;

  static public <A extends JTextComponent> A setText(A c, Object text) {
    setText((JComponent) c, text);
    return c;
  }

  static public <A extends JComboBox> A setText(final A c, Object text) {
    final String s = strUnnull(text);
    {
      swing(() -> {
        c.getEditor().setItem(s);
      });
    }
    return c;
  }

  static public void setText(JLabel c, Object text) {
    setText((JComponent) c, text);
  }

  static public JButton setText(JButton c, Object text) {
    setText((JComponent) c, jlabel_textAsHTML_center_ifNeeded(strUnnull(text)));
    return c;
  }

  static public <A extends JComponent> A setText(final A c, Object text) {
    if (c == null)
      return null;
    final String s = strUnnull(text);
    {
      swing(() -> {
        if (!setText_opt || neq(callOpt(c, "getText"), s))
          call(c, "setText", s);
      });
    }
    return c;
  }

  static public RuntimeException rethrowAndAppendToMessage(Throwable t, String msg) {
    String haveMsg = t.getMessage();
    if (empty(msg) || endsWith(haveMsg, " " + msg))
      throw rethrow(t);
    throw new RuntimeException(joinWithSpace(t.getMessage(), msg), t);
  }

  static public String squareBracketed(String s) {
    return "[" + s + "]";
  }

  static public String spaceCombine(Object... l) {
    return joinNemptiesWithSpace(flattenCollections(ll(l)));
  }

  static public <A, B> AutoCloseable tempRestoreMap(Map<A, B> map) {
    if (map == null)
      return null;
    var cloned = cloneMap(map);
    return () -> copyMap(cloned, map);
  }

  static public boolean is(String a, String b) {
    return false;
  }

  static public void _close(AutoCloseable c) {
    if (c != null)
      try {
        c.close();
      } catch (Throwable e) {
        if (c instanceof javax.imageio.stream.ImageOutputStream)
          return;
        else
          throw rethrow(e);
      }
  }

  static public String assertIdentifier(String s) {
    return assertIsIdentifier(s);
  }

  static public String assertIdentifier(String msg, String s) {
    return assertIsIdentifier(msg, s);
  }

  static public <A, B> Map<A, B> putOrCreateLinkedHashMap(Map<A, B> map, A key, B value) {
    if (map == null)
      map = new LinkedHashMap();
    map.put(key, value);
    return map;
  }

  static public boolean isIdentifier(String s) {
    return isJavaIdentifier(s);
  }

  static public Class typeToClass(Type type) {
    if (type == null)
      return null;
    if (type instanceof Class)
      return ((Class) type);
    if (type instanceof ParameterizedType)
      return optCast(Class.class, ((ParameterizedType) type).getRawType());
    if (type instanceof GenericArrayType)
      return typeToClass(((GenericArrayType) type).getGenericComponentType()).arrayType();
    if (type instanceof IResolvableClass)
      return ((IResolvableClass) type).resolveToClass();
    return null;
  }

  static public void printVars(Object... params) {
    printVars_str(params);
  }

  static public Object metaGet(IMeta o, Object key) {
    return metaMapGet(o, key);
  }

  static public Object metaGet(Object o, Object key) {
    return metaMapGet(o, key);
  }

  static public Object metaGet(String key, IMeta o) {
    return metaMapGet(o, key);
  }

  static public Object metaGet(String key, Object o) {
    return metaMapGet(o, key);
  }

  static public void scaffoldCalled(Object obj, Object function, Object... args) {
    printShortenedFunctionCall(200, (obj == null ? "" : shorten(20, str(obj)) + " :: ") + function, args);
  }

  static public boolean startsWithDigit(String s) {
    return nempty(s) && isDigit(s.charAt(0));
  }

  static public boolean isQuoted(String s) {
    if (isNormalQuoted(s))
      return true;
    return isMultilineQuoted(s);
  }

  static public boolean swic(String a, String b) {
    return startsWithIgnoreCase(a, b);
  }

  static public boolean swic(String a, String b, Matches m) {
    if (!swic(a, b))
      return false;
    m.m = new String[] { substring(a, l(b)) };
    return true;
  }

  static public int parseHexInt(String s) {
    return hexToInt(s);
  }

  static public int intFromBinary(String s) {
    return Integer.parseInt(s, 2);
  }

  static public boolean isInteger(String s) {
    int n = l(s);
    if (n == 0)
      return false;
    int i = 0;
    if (s.charAt(0) == '-')
      if (++i >= n)
        return false;
    while (i < n) {
      char c = s.charAt(i);
      if (c < '0' || c > '9')
        return false;
      ++i;
    }
    return true;
  }

  static public Number parseIntOrLong(String s) {
    long l = parseLong(s);
    return longIsInt(l) ? (Number) (int) l : (Number) l;
  }

  static public float parseFloat(String s) {
    return Float.parseFloat(s);
  }

  static public boolean endsWith(String a, String b) {
    return a != null && a.endsWith(b);
  }

  static public boolean endsWith(String a, char c) {
    return nempty(a) && lastChar(a) == c;
  }

  static public boolean endsWith(String a, String b, Matches m) {
    if (!endsWith(a, b))
      return false;
    m.m = new String[] { dropLast(l(b), a) };
    return true;
  }

  static public long parseLong(String s) {
    if (empty(s))
      return 0;
    return Long.parseLong(dropSuffix("L", s));
  }

  static public long parseLong(Object s) {
    return Long.parseLong((String) s);
  }

  static public boolean hasStaticMethodNamed(Class c, String method) {
    if (c == null)
      return false;
    var methods = getMethodCache(c).cache.get(method);
    if (methods != null)
      for (var m : methods) if (isStaticMethod(m))
        return true;
    return false;
  }

  static public boolean isInterface(Class c) {
    return c != null && c.isInterface();
  }

  static public Field getField(Object o, String field) {
    if (o == null)
      return null;
    return setOpt_findField(_getClass(o), field);
  }

  static public Object getField(Field field, Object o) {
    return fieldGet(field, o);
  }

  static public Field findFieldInInterfaces(Class c, String field) {
    if (c == null)
      return null;
    for (var intf : allInterfacesImplementedBy(c)) {
      var __1 = getField(intf, field);
      if (__1 != null)
        return __1;
    }
    return null;
  }

  static public boolean isStaticField(Field f) {
    return (f.getModifiers() & Modifier.STATIC) != 0;
  }

  static public String[] toStringArray(Collection<String> c) {
    String[] a = new String[l(c)];
    Iterator<String> it = c.iterator();
    for (int i = 0; i < l(a); i++) a[i] = it.next();
    return a;
  }

  static public String[] toStringArray(Object o) {
    if (o instanceof String[])
      return (String[]) o;
    else if (o instanceof Collection)
      return toStringArray((Collection<String>) o);
    else
      throw fail("Not a collection or array: " + getClassName(o));
  }

  static public <A, B> Set<A> keys(Map<A, B> map) {
    return map == null ? new HashSet() : map.keySet();
  }

  static public Set keys(Object map) {
    return keys((Map) map);
  }

  static public <A> Set<A> keys(MultiSet<A> ms) {
    return ms == null ? null : ms.keySet();
  }

  static public <A, B> Set<A> keys(IMultiMap<A, B> mm) {
    return mm == null ? null : mm.keySet();
  }

  static public List<Constructor> constructorsWithNumberOfArguments(Class c, int n) {
    return filter(getDeclaredConstructors_cached(c), con -> l(con.getParameterTypes()) == n);
  }

  static public Object[] toArray(Collection c) {
    return toObjectArray(c);
  }

  static public <A> A[] toArray(Class<A> type, Iterable<A> c) {
    return toArray(c, type);
  }

  static public <A> A[] toArray(Class<A> type, Collection<A> c) {
    return toArray(c, type);
  }

  static public <A> A[] toArray(Collection<A> c, Class<A> type) {
    A[] a = arrayOfType(l(c), type);
    if (a.length == 0)
      return a;
    asList(c).toArray(a);
    return a;
  }

  static public <A> A[] toArray(Iterable<A> c, Class<A> type) {
    var c2 = asList(c);
    A[] a = arrayOfType(l(c2), type);
    if (a.length == 0)
      return a;
    c2.toArray(a);
    return a;
  }

  static public <A> A[] toArray(A[] array, Collection c) {
    if (array == null || c == null)
      return null;
    asList(c).toArray(array);
    return array;
  }

  static public <A> A[] toArrayOrNull(Class<A> type, Collection<A> c) {
    if (empty(c))
      return null;
    return toArray(type, c);
  }

  static public Map<String, Class> parsePrimitiveType_map = litmap("int", int.class, "char", char.class, "byte", byte.class, "short", short.class, "long", long.class, "float", float.class, "double", double.class, "bool", boolean.class);

  static public Class parsePrimitiveType(String s) {
    return parsePrimitiveType_map.get(s);
  }

  static public String joinSubList(List<String> l, int i, int j) {
    return join(subList(l, i, j));
  }

  static public String joinSubList(List<String> l, int i) {
    return join(subList(l, i));
  }

  static public String joinSubList(List<String> l, IntRange r) {
    return r == null ? null : joinSubList(l, r.start, r.end);
  }

  static public Map<String, Class> classForName_cache = synchroHashMap();

  static public Class classForName(String name) {
    return classForName(name, null);
  }

  static public Class classForName(String name, Object classFinder) {
    if (classForName_cache == null || classFinder != null)
      return classForName_uncached(name, classFinder);
    Class c = classForName_cache.get(name);
    if (c == null)
      classForName_cache.put(name, c = classForName_uncached(name, null));
    return c;
  }

  static public Class classForName_uncached(String name, Object classFinder) {
    try {
      if (classFinder != null)
        return (Class) callF(classFinder, name);
      return Class.forName(name);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Class classForNameOpt_noCache(String name) {
    try {
      try {
        return Class.forName(name);
      } catch (ClassNotFoundException e) {
        return null;
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public boolean hasMethodNamed(Object obj, String method) {
    if (obj == null)
      return false;
    if (obj instanceof Class)
      return hasMethodNamed((Class) obj, method);
    return hasMethodNamed(obj.getClass(), method);
  }

  static public boolean hasMethodNamed(Class c, String method) {
    if (c == null)
      return false;
    return getMethodCache(c).cache.containsKey(method);
  }

  static public <A, B> Collection<B> values(Map<A, B> map) {
    return map == null ? emptyList() : map.values();
  }

  static public Collection values(Object map) {
    return values((Map) map);
  }

  static public <A, B> Collection<B> values(MultiMap<A, B> mm) {
    return mm == null ? emptyList() : concatLists(values(mm.data));
  }

  static public <A> List<A> nonNulls(Iterable<A> l) {
    return withoutNulls(l);
  }

  static public <A> List<A> nonNulls(A[] l) {
    return withoutNulls(l);
  }

  static public <A, B> Map<A, B> nonNulls(Map<A, B> map) {
    return withoutNulls(map);
  }

  static public <A, B> List<B> nonNulls(Iterable<A> l, IF1<A, B> f) {
    return mapNonNulls(l, f);
  }

  static public <A, B, C> Map<B, C> mapToMap(IF1<A, Pair<B, C>> f, Iterable<A> l) {
    Map<B, C> map = new HashMap();
    for (A o : unnullForIteration(l)) {
      Pair<B, C> p = f.get(o);
      map.put(p.a, p.b);
    }
    return map;
  }

  static public <A, B, C> Map<B, C> mapToMap(Iterable<A> l, IF1<A, Pair<B, C>> f) {
    return mapToMap(f, l);
  }

  static public <A, B, C, D> Map<C, D> mapToMap(Map<A, B> m, IF2<A, B, Pair<C, D>> f) {
    return mapMapToMap(f, m);
  }

  static public <A, B, C, D> Map<C, D> mapToMap(IF2<A, B, Pair<C, D>> f, Map<A, B> m) {
    return mapMapToMap(f, m);
  }

  static public <A, B> Map<A, B> mapWithSingleValue(Iterable<A> l, B b) {
    HashMap<A, B> map = new HashMap();
    if (l != null)
      for (A a : l) map.put(a, b);
    return map;
  }

  static public HashMap litmap(Object... x) {
    HashMap map = new HashMap();
    litmap_impl(map, x);
    return map;
  }

  static public void litmap_impl(Map map, Object... x) {
    if (x != null)
      for (int i = 0; i < x.length - 1; i += 2) if (x[i + 1] != null)
        map.put(x[i], x[i + 1]);
  }

  static public <A, B> AutoCloseable tempMapPutAll(Map<A, B> map, Map<A, B> toAdd) {
    if (map != null && nempty(toAdd)) {
      List<Pair<A, B>> toRestore = new ArrayList();
      for (var __0 : _entrySet(toAdd)) {
        var key = __0.getKey();
        var value = __0.getValue();
        if (key != null && value != null) {
          B old = map.put(key, value);
          if (!eq(old, value))
            toRestore.add(pair(key, old));
        }
      }
      return () -> {
        for (var p : toRestore) mapPutOrRemove(map, p.a, p.b);
      };
    }
    return null;
  }

  static public <A, B> List<A> keysList(Map<A, B> map) {
    return cloneListSynchronizingOn(keys(map), map);
  }

  static public <A> List<A> keysList(MultiSet<A> ms) {
    return ms == null ? null : keysList(ms.map);
  }

  static public <A, B> B[] valuesArray(Class<B> valueType, Map<A, B> map) {
    if (map == null)
      return null;
    synchronized (collectionMutex(map)) {
      int n = map.size();
      B[] array = newArray(valueType, n);
      var it = valueIterator(map);
      for (int i = 0; i < n; i++) array[i] = it.next();
      return array;
    }
  }

  static public <B, A extends B> A addAndReturn(Collection<B> c, A a) {
    if (c != null)
      c.add(a);
    return a;
  }

  static public <B, A extends B> A addAndReturn(List<B> c, int idx, A a) {
    if (c != null)
      c.add(idx, a);
    return a;
  }

  static public TreeSet mapToTreeSet(Object f, Iterable l) {
    TreeSet x = new TreeSet();
    if (l != null)
      for (Object o : l) x.add(callF(f, o));
    return x;
  }

  static public <A, B> TreeSet<B> mapToTreeSet(IF1<A, B> f, Iterable<A> l) {
    TreeSet<B> x = new TreeSet();
    if (l != null)
      for (var o : l) x.add(f.get(o));
    return x;
  }

  static public <A, B> TreeSet<B> mapToTreeSet(Iterable<A> l, IF1<A, B> f) {
    return mapToTreeSet(f, l);
  }

  static public boolean isAnonymousClass(Class c) {
    return isAnonymousClassName(className(c));
  }

  static public <A> TreeSet<A> asTreeSet(Collection<A> set) {
    return set == null ? null : set instanceof TreeSet ? (TreeSet) set : new TreeSet(set);
  }

  static public boolean isAnonymousClassName(String s) {
    for (int i = 0; i < l(s); i++) if (s.charAt(i) == '$' && Character.isDigit(s.charAt(i + 1)))
      return true;
    return false;
  }

  static public String longestPrefixInTreeSet(String s, TreeSet<String> set) {
    return longestPrefixInNavigableSet(s, set);
  }

  static public String dropPrefix(String prefix, String s) {
    return s == null ? null : s.startsWith(prefix) ? s.substring(l(prefix)) : s;
  }

  static public int smartIndexOf(String s, String sub, int i) {
    if (s == null)
      return 0;
    i = s.indexOf(sub, min(i, l(s)));
    return i >= 0 ? i : l(s);
  }

  static public int smartIndexOf(String s, int i, char c) {
    return smartIndexOf(s, c, i);
  }

  static public int smartIndexOf(String s, char c, int i) {
    if (s == null)
      return 0;
    i = s.indexOf(c, min(i, l(s)));
    return i >= 0 ? i : l(s);
  }

  static public int smartIndexOf(String s, String sub) {
    return smartIndexOf(s, sub, 0);
  }

  static public int smartIndexOf(String s, char c) {
    return smartIndexOf(s, c, 0);
  }

  static public <A> int smartIndexOf(List<A> l, A sub) {
    return smartIndexOf(l, sub, 0);
  }

  static public <A> int smartIndexOf(List<A> l, int start, A sub) {
    return smartIndexOf(l, sub, start);
  }

  static public <A> int smartIndexOf(List<A> l, A sub, int start) {
    int i = indexOf(l, sub, start);
    return i < 0 ? l(l) : i;
  }

  static public Map<String, String> javaxClassShortcuts_cache;

  static public Map<String, String> javaxClassShortcuts() {
    if (javaxClassShortcuts_cache == null)
      javaxClassShortcuts_cache = javaxClassShortcuts_load();
    return javaxClassShortcuts_cache;
  }

  static public Map<String, String> javaxClassShortcuts_load() {
    return litmap("O", "Object", "S", "String", "L", "List", "Cl", "Collection", "Int", "Integer");
  }

  static public <A> List<A> itemPlus(A a, Collection<A> l) {
    return itemPlusList(a, l);
  }

  static public List<String> standardImports_fullyImportedPackages() {
    return endingWith_dropSuffix(standardImports(), ".*");
  }

  static public List<String> standardImports_singleClasses() {
    return notEndingWith(standardImports(), ".*");
  }

  static public Field findFieldOfClass(Class c, String field) {
    return c == null || field == null ? null : getOpt_getFieldMap(c).get(field);
  }

  static public boolean classHasMethodNamed(Class c, String method) {
    if (c == null)
      return false;
    return getMethodCache(c).cache.containsKey(method);
  }

  static public boolean scaffoldingEnabled(Object o) {
    return metaGet(o, "scaffolding") != null;
  }

  static public <A, B> AutoCloseable tempMapPut(Map<A, B> map, A key, B value) {
    if (map != null && key != null && value != null) {
      B old = map.put(key, value);
      return () -> mapPutOrRemove(map, key, old);
    }
    return null;
  }

  static public File getBytecodePathForClass(Object o) {
    return getBytecodePathForClass(_getClass(o));
  }

  static public File getBytecodePathForClass(Class c) {
    try {
      return c == null ? null : new File(c.getProtectionDomain().getCodeSource().getLocation().toURI());
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public String unquote_relaxedMLS(String s) {
    if (s == null)
      return null;
    if (startsWith(s, '[')) {
      int i = 1;
      while (i < s.length() && s.charAt(i) == '=') ++i;
      if (i < s.length() && s.charAt(i) == '[') {
        String m = s.substring(1, i);
        int n = s.length();
        if (s.endsWith("]" + m + "]"))
          n -= i + 1;
        return s.substring(i + 1, n);
      }
    }
    return unquoteSingleOrDoubleQuotes(s);
  }

  static public String intern(String s) {
    return fastIntern(s);
  }

  static public <A, B> Map<A, B> putAll(Map<A, B> a, Map<? extends A, ? extends B> b) {
    if (a != null && b != null)
      a.putAll(b);
    return a;
  }

  static public <A, B> MultiMap<A, B> putAll(MultiMap<A, B> a, Map<? extends A, ? extends B> b) {
    if (a != null)
      a.putAll((Map) b);
    return a;
  }

  static public <A, B> Map<A, B> putAll(Map<A, B> a, Object... b) {
    if (a != null)
      litmap_impl(a, b);
    return a;
  }

  static public double sqrt(double x) {
    return Math.sqrt(x);
  }

  static public double fracNonNeg(double d) {
    return frac_nonNeg(d);
  }

  static public double twoPi() {
    return Math.PI * 2;
  }

  static public String plusPrefixUnlessMinus(String s) {
    return startsWith(s, "-") ? s : "+" + s;
  }

  static public <A> A getAndClear(IVar<A> v) {
    A a = v.get();
    v.set(null);
    return a;
  }

  static public <A, B> Set<A> keySet(Map<A, B> map) {
    return map == null ? new HashSet() : map.keySet();
  }

  static public Set keySet(Object map) {
    return keys((Map) map);
  }

  static public <A> Set<A> keySet(MultiSet<A> ms) {
    return ms.keySet();
  }

  static public <A, B> Set<A> keySet(MultiMap<A, B> mm) {
    return mm.keySet();
  }

  static public <A, B> int keysSize(MultiMap<A, B> mm) {
    return lKeys(mm);
  }

  static public <A> A reverseGet(List<A> l, int idx) {
    if (l == null || idx < 0)
      return null;
    int n = l(l);
    return idx < n ? l.get(n - 1 - idx) : null;
  }

  static public <A, B> Map<A, B> cloneMap(Map<A, B> map) {
    if (map == null)
      return new HashMap();
    synchronized (map) {
      return map instanceof TreeMap ? new TreeMap((TreeMap) map) : map instanceof LinkedHashMap ? new LinkedHashMap(map) : new HashMap(map);
    }
  }

  static public <A, B> List<B> cloneMap(Iterable<A> l, IF1<A, B> f) {
    List x = emptyList(l);
    if (l != null)
      for (A o : cloneList(l)) x.add(f.get(o));
    return x;
  }

  static public <A, B, C extends Collection<B>> List<B> allValues(Map<A, C> map) {
    List<B> out = new ArrayList();
    for (var l : values(map)) addAll(out, l);
    return out;
  }

  static public <A> List<A> concatLists(Iterable<A>... lists) {
    List<A> l = new ArrayList();
    if (lists != null)
      for (Iterable<A> list : lists) addAll(l, list);
    return l;
  }

  static public <A> List<A> concatLists(Collection<? extends Iterable<A>> lists) {
    List<A> l = new ArrayList();
    if (lists != null)
      for (Iterable<A> list : lists) addAll(l, list);
    return l;
  }

  static public <A> List<A> replace(List<A> l, A a, A b) {
    for (int i = 0; i < l(l); i++) if (eq(l.get(i), a))
      l.set(i, b);
    return l;
  }

  static public <A> List<A> replace(A a, A b, List<A> l) {
    return replace(l, a, b);
  }

  static public String replace(String s, String a, String b) {
    return s == null ? null : a == null || b == null ? s : s.replace(a, b);
  }

  static public String replace(String s, char a, char b) {
    return s == null ? null : s.replace(a, b);
  }

  static public boolean hasNext(Iterator it) {
    return it != null && it.hasNext();
  }

  static public Method hashMap_findKey_method;

  static public <A, B> A hashMap_findKey(HashMap<A, B> map, Object key) {
    try {
      if (hashMap_findKey_method == null)
        hashMap_findKey_method = findMethodNamed(HashMap.class, "getNode");
      Map.Entry<A, B> entry = (Map.Entry) hashMap_findKey_method.invoke(map, hashMap_internalHash(key), key);
      return entry == null ? null : entry.getKey();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public String find(String pattern, String text) {
    Matcher matcher = Pattern.compile(pattern).matcher(text);
    if (matcher.find())
      return matcher.group(1);
    return null;
  }

  static public <A> A find(Collection<A> c, Object... data) {
    for (A x : c) if (checkFields(x, data))
      return x;
    return null;
  }

  static public String a(String noun) {
    if (eq(noun, ""))
      return "?";
    return ("aeiou".indexOf(noun.charAt(0)) >= 0 ? "an " : "a ") + noun;
  }

  static public String a(String contents, Object... params) {
    return hfulltag("a", contents, params);
  }

  static public String b(Object contents, Object... params) {
    return fulltag("b", contents, params);
  }

  static public int hashCodeFor(Object a) {
    return a == null ? 0 : a.hashCode();
  }

  static public String joinNemptiesWithColon(String... strings) {
    return joinNempties(": ", strings);
  }

  static public String joinNemptiesWithColon(Collection<String> strings) {
    return joinNempties(": ", strings);
  }

  static public String commaCombine(Object... l) {
    return joinNemptiesWithComma(flattenCollectionsAndArrays(l));
  }

  static public WidthAndHeight widthAndHeight(BufferedImage image) {
    return image == null ? null : widthAndHeight(image.getWidth(), image.getHeight());
  }

  static public WidthAndHeight widthAndHeight(int w) {
    return widthAndHeight(w, w);
  }

  static public WidthAndHeight widthAndHeight(int w, int h) {
    return new WidthAndHeightFinal(w, h);
  }

  static public Pt ptMinus(Pt a, Pt b) {
    if (b == null)
      return a;
    return new Pt(a.x - b.x, a.y - b.y);
  }

  static public UnsupportedOperationException unsupportedOperation() {
    throw new UnsupportedOperationException();
  }

  static public int rgbRed(int rgb) {
    return (rgb >> 16) & 0xFF;
  }

  static public int rgbGreen(int rgb) {
    return (rgb >> 8) & 0xFF;
  }

  static public int rgbBlue(int rgb) {
    return rgb & 0xFF;
  }

  static public Color getColor(BufferedImage img, int x, int y) {
    return colorFromRGBA(img.getRGB(x, y));
  }

  static public Color getColor(BufferedImage img, Pt p) {
    return colorFromRGBA(img.getRGB(p.x, p.y));
  }

  static public float clampZeroToOne(float x) {
    return x < 0 ? 0 : x > 1 ? 1 : x;
  }

  static public double clampZeroToOne(double x) {
    return x < 0 ? 0 : x > 1 ? 1 : x;
  }

  static public int rgbInt(int r, int g, int b) {
    return (clamp(r, 0, 255) << 16) | (clamp(g, 0, 255) << 8) | clamp(b, 0, 255);
  }

  static public int rgbInt(byte r, byte g, byte b) {
    return (ubyteToInt(r) << 16) | (ubyteToInt(g) << 8) | ubyteToInt(b);
  }

  static public int asInt(Object o) {
    return toInt(o);
  }

  static public int shorten_default = 100;

  static public String shorten(CharSequence s) {
    return shorten(s, shorten_default);
  }

  static public String shorten(CharSequence s, int max) {
    return shorten(s, max, "...");
  }

  static public String shorten(CharSequence s, int max, String shortener) {
    if (s == null)
      return "";
    if (max < 0)
      return str(s);
    return s.length() <= max ? str(s) : subCharSequence(s, 0, min(s.length(), max - l(shortener))) + shortener;
  }

  static public String shorten(int max, CharSequence s) {
    return shorten(s, max);
  }

  static public String nlToSpace(String s) {
    return newLinesToSpaces_trim(s);
  }

  static public FlexibleVarContext flexibleVarContextFromParams(Object... params) {
    FlexibleVarContext ctx = new FlexibleVarContext();
    for (int i = 0; i < l(params); i += 2) ctx.put((String) params[i], params[i + 1]);
    return ctx;
  }

  static public long incAtomicLong(AtomicLong l) {
    return l.incrementAndGet();
  }

  static public String n2(long l) {
    return formatWithThousands(l);
  }

  static public String n2(AtomicLong l) {
    return n2(l.get());
  }

  static public String n2(Collection l) {
    return n2(l(l));
  }

  static public String n2(Map map) {
    return n2(l(map));
  }

  static public String n2_getPlural(String singular) {
    return plural(singular);
  }

  static public String n2(double l, String singular) {
    return empty(singular) ? str(l) : n2(l, singular, n2_getPlural(singular));
  }

  static public String n2(double l, String singular, String plural) {
    if (fraction(l) == 0)
      return n2((long) l, singular, plural);
    else
      return l + " " + plural;
  }

  static public String n2(long l, String singular, String plural) {
    return n_fancy2(l, singular, plural);
  }

  static public String n2(long l, String singular) {
    return empty(singular) ? n2(l) : n_fancy2(l, singular, n2_getPlural(singular));
  }

  static public String n2(Collection l, String singular) {
    return n2(l(l), singular);
  }

  static public String n2(Collection l, String singular, String plural) {
    return n_fancy2(l, singular, plural);
  }

  static public String n2(Map m, String singular, String plural) {
    return n_fancy2(m, singular, plural);
  }

  static public String n2(Map m, String singular) {
    return n2(l(m), singular);
  }

  static public String n2(long[] a, String singular) {
    return n2(l(a), singular);
  }

  static public String n2(Object[] a, String singular) {
    return n2(l(a), singular);
  }

  static public String n2(Object[] a, String singular, String plural) {
    return n_fancy2(a, singular, plural);
  }

  static public String n2(MultiSet ms, String singular) {
    return n2(ms, singular, n2_getPlural(singular));
  }

  static public String n2(MultiSet ms, String singular, String plural) {
    return n_fancy2(ms, singular, plural);
  }

  static public String n2(IMultiMap mm, String singular) {
    return n2(mm, singular, n2_getPlural(singular));
  }

  static public String n2(IMultiMap mm, String singular, String plural) {
    return n_fancy2(l(mm), singular, plural);
  }

  static public <A, B> B mapGet(Map<A, B> map, A a) {
    return map == null || a == null ? null : map.get(a);
  }

  static public <A, B> B mapGet(A a, Map<A, B> map) {
    return map == null || a == null ? null : map.get(a);
  }

  static public boolean preciseNuObject_debug = false;

  static public <A> A preciseNuObject(Class<A> c, Object... args) {
    try {
      Constructor[] methods = getDeclaredConstructors_cached(c);
      if (methods.length == 0)
        throw fail(c + " doesn't define any constructors");
      return preciseNuObject(methods, args);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public <A> A preciseNuObject(Constructor[] methods, Object... args) {
    try {
      Constructor best = null;
      int bestScore = Integer.MAX_VALUE;
      boolean widening = false;
      if (preciseNuObject_debug)
        printVars("preciseNuObject", "c", first(methods).getDeclaringClass(), "methods", l(methods));
      for (var m : methods) {
        int score = methodApplicabilityScore_withPrimitiveWidening(m, args);
        if (score == 0)
          return (A) m.newInstance(args);
        if (preciseNuObject_debug)
          print("Method score: " + m + " " + score);
        if (abs(score) < bestScore) {
          best = m;
          bestScore = abs(score);
          widening = score < 0;
        }
      }
      if (best != null)
        if (widening)
          return (A) invokeConstructorWithWidening(best, args);
        else
          return (A) best.newInstance(args);
      methodSearch: for (var m : methods) {
        {
          if (!(m.isVarArgs()))
            continue;
        }
        Object[] newArgs = massageArgsForVarArgsCall(m, args);
        if (newArgs != null)
          return (A) m.newInstance(newArgs);
      }
      var c = first(methods).getDeclaringClass();
      throw fail("No matching constructor found: " + formatFunctionCall(c, map(__51 -> _getClass(__51), args)));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public String formatFunctionCall(String fname, Object... args) {
    return formatFunctionCall((Object) fname, args);
  }

  static public String formatFunctionCall(Object fname, Object... args) {
    return fname + "(" + joinWithComma(allToString(args)) + ")";
  }

  static public String formatFunctionCall(String fname, Iterable args) {
    return formatFunctionCall((Object) fname, args);
  }

  static public String formatFunctionCall(Object fname, Iterable args) {
    return formatFunctionCall(fname, toObjectArray(args));
  }

  static public String resolve(String host) {
    return hostToIP(host);
  }

  static public String strOrClassName(Object o) {
    if (o instanceof Class)
      return className((Class) o);
    return str(o);
  }

  static public String toStringWithClass(Object o) {
    return o == null ? null : className(o) + " - " + o;
  }

  static public <A> A _get(List<A> l, int idx) {
    return l != null && idx >= 0 && idx < l(l) ? l.get(idx) : null;
  }

  static public Object _get(Object o, String field) {
    return get(o, field);
  }

  static public Object _get(String field, Object o) {
    return get(o, field);
  }

  static public <A> A _get(A[] l, int idx) {
    return idx >= 0 && idx < l(l) ? l[idx] : null;
  }

  static public boolean newPreciseCall_debug = false;

  static public Object newPreciseCall(Object o, String method, Object... args) {
    try {
      if (o == null)
        return null;
      boolean staticCall = o instanceof Class;
      Class c = staticCall ? (Class) o : o.getClass();
      Object target = staticCall ? null : o;
      _MethodCache cache = callOpt_getCache(c);
      List<Method> methods = cache.cache.get(method);
      Method best = null;
      int bestScore = Integer.MAX_VALUE;
      boolean widening = false;
      if (newPreciseCall_debug)
        printVars("newPreciseCall", "method", method, "staticCall", staticCall, "c", c, "methods", l(methods));
      if (methods != null)
        for (Method m : methods) {
          if (staticCall && !isStaticMethod(m))
            continue;
          int score = methodApplicabilityScore_withPrimitiveWidening(m, args);
          if (score == 0)
            return invokeMethod(m, target, args);
          if (newPreciseCall_debug)
            print("Method score: " + m + " " + score);
          if (abs(score) < bestScore) {
            best = m;
            bestScore = abs(score);
            widening = score < 0;
          }
        }
      if (best != null)
        if (widening)
          return invokeMethodWithWidening(best, target, args);
        else
          return invokeMethod(best, target, args);
      return callWithVarargs(o, method, args);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public List<Method> findMethodsNamed_cached(Object obj, String method) {
    return findMethodsNamed_cached(_getClass(obj), method);
  }

  static public List<Method> findMethodsNamed_cached(Class c, String method) {
    if (c == null)
      return null;
    return getMethodCache(c).cache.get(method);
  }

  static public <A> boolean any(Object pred, Iterable<A> l) {
    if (l != null)
      for (A a : l) if (isTrue(callF(pred, a)))
        return true;
    return false;
  }

  static public <A> boolean any(IF1<A, Boolean> pred, Iterable<A> l) {
    if (l != null)
      for (A a : l) if (pred.get(a))
        return true;
    return false;
  }

  static public <A> boolean any(Iterable<A> l, IF1<A, Boolean> pred) {
    return any(pred, l);
  }

  static public <A> boolean any(A[] l, IF1<A, Boolean> pred) {
    if (l != null)
      for (A a : l) if (pred.get(a))
        return true;
    return false;
  }

  static public boolean any(Iterable<Boolean> l) {
    if (l != null)
      for (Boolean a : l) if (isTrue(a))
        return true;
    return false;
  }

  static public Pair<Method, Boolean> findMethod_withPrimitiveWidening_onTypes(Object o, String method, Class... argTypes) {
    try {
      Lowest<Method> best = new Lowest();
      boolean widening = false;
      if (o instanceof Class) {
        _MethodCache cache = callOpt_getCache((Class) o);
        List<Method> methods = cache.cache.get(method);
        if (methods != null)
          for (Method m : methods) {
            {
              if (!(isStaticMethod(m)))
                continue;
            }
            int score = methodApplicabilityScore_withPrimitiveWidening_onTypes(m, argTypes);
            if (abs(score) < Integer.MAX_VALUE) {
              best.put(m, abs(score));
              widening = score < 0;
            }
          }
        return pair(best.get(), widening);
      }
      if (o == null)
        return null;
      _MethodCache cache = callOpt_getCache(o.getClass());
      List<Method> methods = cache.cache.get(method);
      if (methods != null)
        for (Method m : methods) {
          int score = methodApplicabilityScore_withPrimitiveWidening_onTypes(m, argTypes);
          if (abs(score) < Integer.MAX_VALUE) {
            best.put(m, abs(score));
            widening = score < 0;
          }
        }
      return pair(best.get(), widening);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public <A> Object[] mapToArrayOrNull(A[] l, IF1<A, Object> f) {
    if (l == null)
      return null;
    int n = l.length;
    if (n == 0)
      return null;
    Object[] array = new Object[n];
    for (int i = 0; i < n; i++) array[i] = f.get(l[i]);
    return array;
  }

  static public <A> Object[] mapToArrayOrNull(IF1<A, Object> f, A[] l) {
    return mapToArrayOrNull(l, f);
  }

  static public <A> Object[] mapToArrayOrNull(IF1<A, Object> f, Collection<A> l) {
    int n = l(l);
    if (n == 0)
      return null;
    Object[] array = new Object[n];
    if (n != 0) {
      Iterator it = iterator(l);
      for (int i = 0; i < n; i++) array[i] = callF(f, it.next());
    }
    return array;
  }

  static public <A> Object[] mapToArrayOrNull(Collection<A> l, IF1<A, Object> f) {
    return mapToArrayOrNull(l, f);
  }

  static public Object newPreciseCall_sentinel(Object o, String method, Object methodNotFoundSentinel, Object... args) {
    try {
      if (o == null)
        return null;
      boolean staticCall = o instanceof Class;
      Class c = staticCall ? (Class) o : o.getClass();
      Object target = staticCall ? null : o;
      _MethodCache cache = callOpt_getCache(c);
      List<Method> methods = cache.cache.get(method);
      Method best = null;
      int bestScore = Integer.MAX_VALUE;
      boolean widening = false;
      if (methods != null)
        for (Method m : methods) {
          if (staticCall && !isStaticMethod(m))
            continue;
          int score = methodApplicabilityScore_withPrimitiveWidening(m, args);
          if (score == 0)
            return invokeMethod(m, target, args);
          if (abs(score) < bestScore) {
            best = m;
            bestScore = abs(score);
            widening = score < 0;
          }
        }
      if (best != null)
        if (widening)
          return invokeMethodWithWidening(best, target, args);
        else
          return invokeMethod(best, target, args);
      return callWithVarargs_sentinel(o, method, methodNotFoundSentinel, args);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public <A> A[] itemPlusArray(A a, A[] l) {
    return singlePlusArray(a, l);
  }

  static public Object preciseGetOrCallMethod_sentinel(Object object, String name, Object sentinel) {
    if (object == null)
      return null;
    if (canCallWithVarargs(object, name))
      return call(object, name);
    Field f = getField(object, name);
    if (f != null)
      return fieldGet(f, object);
    return sentinel;
  }

  static public Method findSingleInterfaceMethodOrFail(Class intrface) {
    Method m = findSingleInterfaceMethod(intrface);
    if (m == null)
      throw fail(intrface + " is not a single method interface");
    return m;
  }

  static public <A> A proxyFromInvocationHandler(Class<A> intrface, InvocationHandler handler) {
    return (A) java.lang.reflect.Proxy.newProxyInstance(intrface.getClassLoader(), new Class[] { intrface }, handler);
  }

  static public Object handleObjectMethodsInProxyInvocationHandler(Object invocationHandler, Method implementedMethod, Method method, Object proxy, Object[] actualArgs) {
    String name = method.getName();
    if (name.equals("hashCode"))
      return invocationHandler.hashCode();
    if (name.equals("equals"))
      return proxy == actualArgs[0];
    if (name.equals("toString"))
      return invocationHandler.toString();
    throw fail("No handler for method " + method + " (only have " + implementedMethod + ")");
  }

  static public Object[] concatMethodArgs(Object[] args1, Object[] args2) {
    int n1, n2;
    if (args1 == null || (n1 = args1.length) == 0)
      return args2;
    if (args2 == null || (n2 = args2.length) == 0)
      return args1;
    Object[] args = new Object[n1 + n2];
    for (int i = 0; i < n1; i++) args[i] = args1[i];
    for (int i = 0; i < n2; i++) args[n1 + i] = args2[i];
    return args;
  }

  static public Object invokeMethodWithWidening(Method m, Object o, Object... args) {
    try {
      try {
        Class<?>[] types = m.getParameterTypes();
        int n = types.length;
        Object[] args2 = new Object[n];
        for (int i = 0; i < n; i++) args2[i] = convertPrimitiveIfNecessary(args[i], types[i]);
        return m.invoke(o, args2);
      } catch (InvocationTargetException e) {
        throw rethrow(getExceptionCause(e));
      } catch (IllegalArgumentException e) {
        throw new IllegalArgumentException(e.getMessage() + " - was calling: " + m + ", args: " + joinWithSpace(classNames(args)));
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Iterator iterator_gen(Object o) {
    if (o == null)
      return emptyItIt();
    if (o instanceof Iterator)
      return ((Iterator) o);
    if (o instanceof Iterable)
      return ((Iterable) o).iterator();
    if (o instanceof Object[])
      return arrayIterator((Object[]) o);
    throw fail("Not iterable: " + className(o));
  }

  static public <A, B, C extends Iterator<B>> IterableIterator<B> nestedIterator(Iterable<A> c, final F1<A, C> makeInnerIterator) {
    return nestedIterator(iterator(c), makeInnerIterator);
  }

  static public <A, B, C extends Iterator<B>> IterableIterator<B> nestedIterator(Iterable<A> c, IF1<A, C> makeInnerIterator) {
    return nestedIterator(iterator(c), makeInnerIterator);
  }

  static public <A, B, C extends Iterator<B>> IterableIterator<B> nestedIterator(IterableIterator<A> c, IF1<A, C> makeInnerIterator) {
    return nestedIterator((Iterator<A>) c, makeInnerIterator);
  }

  static public <A, B, C extends Iterator<B>> IterableIterator<B> nestedIterator(Iterator<A> it1, IF1<A, C> makeInnerIterator) {
    if (it1 == null || !it1.hasNext())
      return emptyItIt();
    return iff(new F0() {

      public A a;

      public Iterator<B> innerIterator;

      {
        nextOuter();
      }

      public void nextOuter() {
        a = it1.next();
        innerIterator = makeInnerIterator.get(a);
      }

      public Object get() {
        while (true) {
          ping();
          if (innerIterator != null && innerIterator.hasNext())
            return innerIterator.next();
          if (!it1.hasNext())
            return endMarker();
          nextOuter();
        }
      }
    });
  }

  static public <A, B, C extends Iterator<B>> IterableIterator<B> nestedIterator(final Iterator<A> it1, F1<A, C> makeInnerIterator) {
    if (it1 == null || !it1.hasNext())
      return emptyItIt();
    return iff(new F0() {

      public A a;

      public Iterator<B> innerIterator;

      {
        nextOuter();
      }

      public void nextOuter() {
        a = it1.next();
        innerIterator = makeInnerIterator.get(a);
      }

      public Object get() {
        while (true) {
          ping();
          if (innerIterator != null && innerIterator.hasNext())
            return innerIterator.next();
          if (!it1.hasNext())
            return endMarker();
          nextOuter();
        }
      }
    });
  }

  static public <A, B, C extends Iterator<B>> IterableIterator<B> nestedIterator(IF1<A, C> makeInnerIterator, Iterator<A> it1) {
    return nestedIterator(it1, makeInnerIterator);
  }

  static public <A, B, C extends Iterator<B>> IterableIterator<B> nestedIterator(IF1<A, C> makeInnerIterator, Collection<A> l) {
    return nestedIterator(l, makeInnerIterator);
  }

  static public String struct(Object o) {
    return structure(o);
  }

  static public String struct(Object o, structure_Data data) {
    return structure(o, data);
  }

  static public List<String> structTok(String s) {
    return javaTok_noMLS(s);
  }

  static public String shortName(Object o) {
    return shortClassName(o);
  }

  static public String replacePrefix(String prefix, String replacement, String s) {
    if (!startsWith(s, prefix))
      return s;
    return replacement + substring(s, l(prefix));
  }

  static public String indentStructureString(String s) {
    return indentStructureString(100, s);
  }

  static public String indentStructureString(int levels, String s) {
    if (s == null)
      return null;
    return new StructureStringIndenter().levels(levels).get(s);
  }

  static public int getWidth(Component c) {
    return c == null ? 0 : (int) swingCall(c, "getWidth");
  }

  static public int getHeight(Component c) {
    return c == null ? 0 : (int) swingCall(c, "getHeight");
  }

  static public Rect rect(int x, int y, int w, int h) {
    return new Rect(x, y, w, h);
  }

  static public Rect rect(Pt p, int w, int h) {
    return new Rect(p.x, p.y, w, h);
  }

  static public Rect rect(int w, int h) {
    return new Rect(0, 0, w, h);
  }

  static public long longMul(long a, long b) {
    return a * b;
  }

  static public <A> ListIterator<A> listIterator(List<A> l) {
    return l == null ? emptyListIterator() : l.listIterator();
  }

  static public <A> List<A> subList(List<A> l, int startIndex) {
    return subList(l, startIndex, l(l));
  }

  static public <A> List<A> subList(int startIndex, List<A> l) {
    return subList(l, startIndex);
  }

  static public <A> List<A> subList(int startIndex, int endIndex, List<A> l) {
    return subList(l, startIndex, endIndex);
  }

  static public <A> List<A> subList(List<A> l, int startIndex, int endIndex) {
    if (l == null)
      return null;
    int n = l(l);
    startIndex = Math.max(0, startIndex);
    endIndex = Math.min(n, endIndex);
    if (startIndex > endIndex)
      return ll();
    if (startIndex == 0 && endIndex == n)
      return l;
    return l.subList(startIndex, endIndex);
  }

  static public <A> List<A> subList(List<A> l, IntRange r) {
    return subList(l, r.start, r.end);
  }

  static public <A, B> boolean containsKey(Map<A, B> map, A key) {
    return map != null && map.containsKey(key);
  }

  static public <A, B> Map<A, B> putOrCreateSyncMap(Map<A, B> map, A key, B value) {
    if (map == null)
      map = syncHashMap();
    map.put(key, value);
    return map;
  }

  static public AutoCloseable tempSet(Object o, final String field, Object value) {
    return tempSetField(o, field, value);
  }

  static public <A, B> AutoCloseable tempPut(Map<A, B> map, A key, B value) {
    if (map != null) {
      boolean wasContained = map.containsKey(key);
      B old = map.put(key, value);
      return () -> {
        if (wasContained)
          map.put(key, old);
        else
          map.remove(key);
      };
    }
    return null;
  }

  static public void assertSame(Object a, Object b) {
    assertSame("", a, b);
  }

  static public void assertSame(String msg, Object a, Object b) {
    if (a != b)
      throw fail(joinNemptiesWithColon(msg, a + " != " + b + " (" + identityHash(a) + "/" + identityHash(b) + ")"));
  }

  static public void assertSame(IF0<String> msg, Object a, Object b) {
    if (a != b)
      throw fail(joinNemptiesWithColon(msg.get(), a + " != " + b + " (" + identityHash(a) + "/" + identityHash(b) + ")"));
  }

  static public LineAndColumn tokenToLineAndColumn(ListAndIndex<String> ptr) {
    return ptr == null ? null : tokenToLineAndColumn(ptr.list(), ptr.idx());
  }

  static public LineAndColumn tokenToLineAndColumn(List<String> tok, int tokenIndex) {
    int line = 1, col = 1;
    tokenIndex = min(tokenIndex, l(tok));
    for (int i = 0; i < tokenIndex; i++) {
      String t = tok.get(i);
      int n = l(t);
      for (int j = 0; j < n; j++) if (t.charAt(j) == '\n') {
        ++line;
        col = 1;
      } else
        ++col;
    }
    return new LineAndColumn(line, col);
  }

  static public String renderLineAndColumnRange(LineAndColumn start, LineAndColumn end) {
    if (eq(start, end))
      return str(start);
    return start + " to " + (end.line == start.line ? end.col : firstToLower(str(end)));
  }

  static public Object metaMapGet(IMeta o, Object key) {
    return o == null ? null : o.metaGet(key);
  }

  static public Object metaMapGet(Object o, Object key) {
    return metaMapGet(toIMeta(o), key);
  }

  static public void metaPut(IMeta o, Object key, Object value) {
    metaMapPut(o, key, value);
  }

  static public void metaPut(Object o, Object key, Object value) {
    metaMapPut(o, key, value);
  }

  static public void metaMapPut(IMeta o, Object key, Object value) {
    {
      if (o != null)
        o.metaPut(key, value);
    }
  }

  static public void metaMapPut(Object o, Object key, Object value) {
    var meta = initIMeta(o);
    {
      if (meta != null)
        meta.metaPut(key, value);
    }
  }

  static public Map convertObjectMetaToMap(IMeta o) {
    return convertObjectMetaToMap(o, () -> makeObjectMetaMap());
  }

  static public Map convertObjectMetaToMap(IMeta o, IF0<Map> createEmptyMap) {
    if (o == null)
      return null;
    Object meta = o._getMeta();
    if (meta instanceof Map)
      return ((Map) meta);
    var mutex = tempMetaMutex(o);
    try {
      var actualMutex = mutex.get();
      synchronized (actualMutex) {
        meta = o._getMeta();
        if (meta instanceof Map)
          return ((Map) meta);
        Map map = createEmptyMap.get();
        if (meta != null)
          map.put("previousMeta", meta);
        o._setMeta(map);
        return map;
      }
    } finally {
      _close(mutex);
    }
  }

  static public <A, B> void syncMapPutOrRemove(Map<A, B> map, A key, B value) {
    syncMapPut2(map, key, value);
  }

  static public File byteCodePathForClass(Class c) {
    if (c == null)
      return null;
    ClassLoader cl = getClassLoader(c);
    Collection<File> files = (Collection<File>) (getOpt(cl, "files"));
    if (files != null) {
      String name = c.getName().replace('.', '/') + ".class";
      for (File location : files) if (dirOrZipContainsPath(location, name))
        return location;
      throw fail(name + " not found in: " + files);
    }
    return null;
  }

  static public File byteCodePathForClass(Object o) {
    return byteCodePathForClass(_getClass(o));
  }

  static public <A> List<A> itemPlusList(A a, Collection<A> l) {
    return concatLists(ll(a), l);
  }

  static public List<String> endingWith_dropSuffix(Collection<String> l, String suffix) {
    List<String> out = new ArrayList();
    for (String s : unnullForIteration(l)) if (endsWith(s, suffix))
      out.add(dropLast(l(suffix), s));
    return out;
  }

  static public List<String> endingWith_dropSuffix(String suffix, Collection<String> l) {
    return endingWith_dropSuffix(l, suffix);
  }

  static public List<String> standardImports_cache;

  static public List<String> standardImports() {
    if (standardImports_cache == null)
      standardImports_cache = standardImports_load();
    return standardImports_cache;
  }

  static public List<String> standardImports_load() {
    return ll("java.util.*", "java.util.zip.*", "java.util.List", "java.util.regex.*", "java.util.concurrent.*", "java.util.concurrent.atomic.*", "java.util.concurrent.locks.*", "java.util.function.*", "javax.swing.*", "javax.swing.event.*", "javax.swing.text.*", "javax.swing.table.*", "java.io.*", "java.net.*", "java.lang.reflect.*", "java.lang.ref.*", "java.lang.management.*", "java.security.*", "java.security.spec.*", "java.awt.*", "java.awt.event.*", "java.awt.image.*", "java.awt.geom.*", "javax.imageio.*", "java.math.*", "java.time.Duration", "java.lang.invoke.VarHandle", "java.lang.invoke.MethodHandles");
  }

  static public List<String> classNamesInJarOrDir(File dir) {
    return classNamesInJarOrDir(dir, "");
  }

  static public List<String> classNamesInJarOrDir(File dir, String prefixInJar) {
    List<String> classes = new ArrayList();
    if (dir == null) {
    } else if (dir.isDirectory()) {
      for (File f : listFiles(dir)) {
        String s = f.getName();
        if (s.endsWith(".class"))
          classes.add(dropSuffix(".class", s));
      }
    } else if (dir.isFile()) {
      try {
        JarFile jarFile = new JarFile(dir);
        try {
          Enumeration<JarEntry> e = jarFile.entries();
          while (e.hasMoreElements()) {
            JarEntry je = e.nextElement();
            if (je.isDirectory() || je.getName().startsWith("META-INF/") || !je.getName().endsWith(".class"))
              continue;
            String className = dropSuffix(".class", je.getName());
            className = dropPrefixOrNull(prefixInJar, className);
            if (className == null)
              continue;
            if (className.contains("-"))
              continue;
            className = className.replace('/', '.');
            classes.add(className);
          }
        } finally {
          jarFile.close();
        }
      } catch (Throwable __e) {
        pcallFail(__e);
      }
    }
    return classes;
  }

  static public List<String> classNamesInLoadedJigsawModules() {
    return concatMap(loadedJigsawModuleNames(), moduleName -> classNamesInJigsawModule(moduleName));
  }

  static volatile public int numberOfCores_value;

  static public int numberOfCores() {
    if (numberOfCores_value == 0)
      numberOfCores_value = Runtime.getRuntime().availableProcessors();
    return numberOfCores_value;
  }

  static public Thread newThread(Object runnable) {
    return new BetterThread(_topLevelErrorHandling(toRunnable(runnable)));
  }

  static public Thread newThread(Object runnable, String name) {
    if (name == null)
      name = defaultThreadName();
    return new BetterThread(_topLevelErrorHandling(toRunnable(runnable)), name);
  }

  static public Thread newThread(String name, Object runnable) {
    return newThread(runnable, name);
  }

  static public <A> A popFirst(List<A> l) {
    if (empty(l))
      return null;
    A a = first(l);
    l.remove(0);
    return a;
  }

  static public <A> A popFirst(Collection<A> l) {
    if (empty(l))
      return null;
    A a = first(l);
    l.remove(a);
    return a;
  }

  static public <A, B> Pair<A, B> popFirst(Map<A, B> map) {
    if (map == null)
      return null;
    var it = map.entrySet().iterator();
    if (!it.hasNext())
      return null;
    var p = mapEntryToPair(it.next());
    it.remove();
    return p;
  }

  static public <A> List<A> popFirst(int n, List<A> l) {
    List<A> part = cloneSubList(l, 0, n);
    removeSubList(l, 0, n);
    return part;
  }

  static public <A> AppendableChain<A> popFirst(AppendableChain<A> a) {
    return a == null ? null : a.popFirst();
  }

  static public <A> Chain<A> chainPlus(Chain<A> chain, A a) {
    return new Chain<A>(a, chain);
  }

  static public <A> Chain<A> chainPlus(Chain<A> chain, A... l) {
    for (A a : unnullForIteration(l)) chain = chainPlus(chain, a);
    return chain;
  }

  static public <A> ReverseChain<A> chainPlus(ReverseChain<A> chain, A a) {
    return new ReverseChain<A>(chain, a);
  }

  static public <A> ReverseChain<A> chainPlus(ReverseChain<A> chain, A... l) {
    for (A a : unnullForIteration(l)) chain = chainPlus(chain, a);
    return chain;
  }

  static public <A> AppendableChain<A> chainPlus(AppendableChain<A> chain, A a) {
    if (chain == null)
      return new AppendableChain<A>(a);
    chain.add(a);
    return chain;
  }

  static public <A> AppendableChain<A> chainPlus(AppendableChain<A> chain, A... l) {
    for (A a : unnullForIteration(l)) chain = chainPlus(chain, a);
    return chain;
  }

  static public void syncNotifyAll(Object o) {
    if (o != null)
      synchronized (o) {
        o.notifyAll();
      }
  }

  static public Type getRawType(Type t) {
    if (t instanceof ParameterizedType)
      return ((ParameterizedType) t).getRawType();
    return t;
  }

  static public <A> Set<A> createOrAddToSet(Set<A> set, A a) {
    if (set == null)
      set = new HashSet();
    set.add(a);
    return set;
  }

  static public <A> Set<A> createOrAddToSet(Set<A> set, A... l) {
    for (A a : unnullForIteration(l)) set = createOrAddToSet(set, a);
    return set;
  }

  static public boolean hasModifier(List<String> tok, String modifier) {
    for (int i = 1; i < tok.size() && getJavaModifiers().contains(tok.get(i)); i += 2) if (tok.get(i).equals(modifier))
      return true;
    return false;
  }

  static public LinkedHashMap litorderedmap(Object... x) {
    LinkedHashMap map = new LinkedHashMap();
    litmap_impl(map, x);
    return map;
  }

  static public String md5(String text) {
    try {
      if (text == null)
        return "-";
      return bytesToHex(md5AsByteArray(toUtf8(text)));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public String md5(byte[] data) {
    if (data == null)
      return "-";
    return bytesToHex(md5AsByteArray(data));
  }

  static public String md5(File file) {
    return md5OfFile(file);
  }

  static public String or2(String a, String b) {
    return nempty(a) ? a : b;
  }

  static public String or2(String a, String b, String c) {
    return or2(or2(a, b), c);
  }

  static public String[] mapToStringArray(Map map) {
    List l = new ArrayList();
    for (Object o : keys(map)) {
      l.add(o);
      l.add(map.get(o));
    }
    return toStringArray(l);
  }

  static public <A> String[] mapToStringArray(IF1<A, String> f, Iterable<A> l) {
    return toStringArray(map(f, l));
  }

  static public <A> String[] mapToStringArray(Iterable<A> l, IF1<A, String> f) {
    return mapToStringArray(f, l);
  }

  static public org.apache.bcel.generic.Type typeToBCELType(Type t) {
    return classToBCELType(getRawTypeClass(t));
  }

  static public String typeToVMSignature(Type t) {
    Class c = getRawTypeClass(t);
    if (t instanceof ParameterizedType) {
      List<Class> args = map(__52 -> getRawTypeClass(__52), ((ParameterizedType) t).getActualTypeArguments());
      if (nempty(args))
        return classNameToByteCodeFormat(c) + "<" + joinMap(args, arg -> classNameToByteCodeFormat(arg) + ";") + ">;";
    }
    return classNameToByteCodeFormat(c) + ";";
  }

  static public org.apache.bcel.generic.Type classToBCELType(Class c) {
    if (c == null)
      return null;
    if (isPrimitiveType(c)) {
      if (c == byte.class)
        return org.apache.bcel.generic.Type.BYTE;
      if (c == char.class)
        return org.apache.bcel.generic.Type.CHAR;
      if (c == short.class)
        return org.apache.bcel.generic.Type.SHORT;
      if (c == int.class)
        return org.apache.bcel.generic.Type.INT;
      if (c == long.class)
        return org.apache.bcel.generic.Type.LONG;
      if (c == double.class)
        return org.apache.bcel.generic.Type.DOUBLE;
      if (c == void.class)
        return org.apache.bcel.generic.Type.VOID;
      if (c == boolean.class)
        return org.apache.bcel.generic.Type.BOOLEAN;
      throw fail("TODO: classToBCELType " + c);
    }
    if (isArrayType(c)) {
      int dimensions = 0;
      while (c.isArray()) {
        ++dimensions;
        c = c.componentType();
      }
      return new ArrayType(classToBCELType(c), dimensions);
    }
    return new ObjectType(className(c));
  }

  static public Object[] mapToArray(Map map) {
    return mapToObjectArray(map);
  }

  static public Object[] mapToArray(Object f, Collection l) {
    return mapToObjectArray(f, l);
  }

  static public Object[] mapToArray(Object f, Object[] l) {
    return mapToObjectArray(f, l);
  }

  static public <A> Object[] mapToArray(Collection<A> l, IF1<A, Object> f) {
    return mapToObjectArray(l, f);
  }

  static public <A> Object[] mapToArray(A[] l, IF1<A, Object> f) {
    return mapToObjectArray(f, l);
  }

  static public <A> Object[] mapToArray(IF1<A, Object> f, A[] l) {
    return mapToObjectArray(f, l);
  }

  static public boolean isPrimitiveType(Class c) {
    return c != null && c.isPrimitive();
  }

  static public Class primitiveToBoxedType(Class type) {
    if (type == boolean.class)
      return Boolean.class;
    if (type == int.class)
      return Integer.class;
    if (type == long.class)
      return Long.class;
    if (type == float.class)
      return Float.class;
    if (type == short.class)
      return Short.class;
    if (type == char.class)
      return Character.class;
    if (type == byte.class)
      return Byte.class;
    if (type == double.class)
      return Double.class;
    return null;
  }

  static public Object[] repArray(Object a, int n) {
    return arrayrep(a, n);
  }

  static public <A> A[] repArray(Class<A> type, A a, int n) {
    return arrayrep(type, a, n);
  }

  static public Field setOpt_findField(Class c, String field) {
    HashMap<String, Field> map;
    synchronized (getOpt_cache) {
      map = getOpt_cache.get(c);
      if (map == null)
        map = getOpt_makeCache(c);
    }
    return map.get(field);
  }

  static public void setOpt(Object o, String field, Object value) {
    try {
      if (o == null)
        return;
      Class c = o.getClass();
      HashMap<String, Field> map;
      if (getOpt_cache == null)
        map = getOpt_makeCache(c);
      else
        synchronized (getOpt_cache) {
          map = getOpt_cache.get(c);
          if (map == null)
            map = getOpt_makeCache(c);
        }
      if (map == getOpt_special) {
        if (o instanceof Class) {
          setOpt((Class) o, field, value);
          return;
        }
        setOpt_raw(o, field, value);
        return;
      }
      Field f = map.get(field);
      if (f != null) {
        smartSet(f, o, value);
        return;
      }
      if (o instanceof DynamicObject) {
        setDyn(((DynamicObject) o), field, value);
        return;
      }
      if (o instanceof IMeta)
        setDyn(((IMeta) o), field, value);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public void setOpt(Class c, String field, Object value) {
    if (c == null)
      return;
    try {
      Field f = setOpt_findStaticField(c, field);
      if (f != null)
        smartSet(f, null, value);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  static public Field setOpt_findStaticField(Class<?> c, String field) {
    Class _c = c;
    do {
      for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0) {
        makeAccessible(f);
        return f;
      }
      _c = _c.getSuperclass();
    } while (_c != null);
    return null;
  }

  static public String resolvableClassToName(Type type) {
    if (type instanceof IResolvableClass)
      return ((IResolvableClass) type).resolveToClassName();
    return null;
  }

  static public int javaTok_n, javaTok_elements;

  static public boolean javaTok_opt = false;

  static public List<String> javaTok(String s) {
    ++javaTok_n;
    ArrayList<String> tok = new ArrayList();
    int l = s == null ? 0 : s.length();
    int i = 0;
    while (i < l) {
      int j = i;
      char c, d;
      while (j < l) {
        c = s.charAt(j);
        d = j + 1 >= l ? '\0' : s.charAt(j + 1);
        if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
          ++j;
        else if (c == '/' && d == '*') {
          do ++j; while (j < l && !regionMatches(s, j, "*/"));
          j = Math.min(j + 2, l);
        } else if (c == '/' && d == '/') {
          do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0);
        } else
          break;
      }
      tok.add(javaTok_substringN(s, i, j));
      i = j;
      if (i >= l)
        break;
      c = s.charAt(i);
      d = i + 1 >= l ? '\0' : s.charAt(i + 1);
      if (c == '\'' && Character.isJavaIdentifierStart(d) && i + 2 < l && "'\\".indexOf(s.charAt(i + 2)) < 0) {
        j += 2;
        while (j < l && Character.isJavaIdentifierPart(s.charAt(j))) ++j;
      } else if (c == '\'' || c == '"') {
        char opener = c;
        ++j;
        while (j < l) {
          int c2 = s.charAt(j);
          if (c2 == opener || c2 == '\n' && opener == '\'') {
            ++j;
            break;
          } else if (c2 == '\\' && j + 1 < l)
            j += 2;
          else
            ++j;
        }
      } else if (Character.isJavaIdentifierStart(c))
        do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || s.charAt(j) == '\''));
      else if (Character.isDigit(c)) {
        do ++j; while (j < l && Character.isDigit(s.charAt(j)));
        if (j < l && s.charAt(j) == 'L')
          ++j;
      } else if (c == '[' && d == '[') {
        do ++j; while (j < l && !regionMatches(s, j, "]]"));
        j = Math.min(j + 2, l);
      } else if (c == '[' && d == '=' && i + 2 < l && s.charAt(i + 2) == '[') {
        do ++j; while (j + 2 < l && !regionMatches(s, j, "]=]"));
        j = Math.min(j + 3, l);
      } else
        ++j;
      tok.add(javaTok_substringC(s, i, j));
      i = j;
    }
    if ((tok.size() % 2) == 0)
      tok.add("");
    javaTok_elements += tok.size();
    return tok;
  }

  static public List<String> javaTok(List<String> tok) {
    return javaTokWithExisting(join(tok), tok);
  }

  static public boolean eqOrEqic(boolean caseInsensitive, String a, String b) {
    return caseInsensitive ? eqic(a, b) : eq(a, b);
  }

  static public String assertInteger(String s) {
    if (!isInteger(s))
      throw fail("Not an integer: " + quote(s));
    return s;
  }

  static public boolean containsLineBreak(String s) {
    return containsNewLine(s);
  }

  static public boolean warn_on = true;

  static public ThreadLocal<List<String>> warn_warnings = new ThreadLocal();

  static public void warn(String s) {
    if (warn_on)
      print("Warning: " + s);
  }

  static public void warn(String s, List<String> warnings) {
    warn(s);
    if (warnings != null)
      warnings.add(s);
    addToCollection(warn_warnings.get(), s);
  }

  static public <A extends Iterable> A pnl(A l) {
    return pnl("", l);
  }

  static public <A extends Iterable> A pnl(String prefix, A l) {
    printNumberedLines(prefix, l);
    return l;
  }

  static public <A> A[] pnl(A[] l) {
    return pnl("", l);
  }

  static public <A> A[] pnl(String prefix, A[] l) {
    printNumberedLines(prefix, l);
    return l;
  }

  static public <A extends Map> A pnl(A map) {
    printNumberedLines(map);
    return map;
  }

  static public <A extends Map> A pnl(String prefix, A map) {
    printNumberedLines(prefix, map);
    return map;
  }

  static public String pnl(String s) {
    printNumberedLines(lines(s));
    return s;
  }

  static public <A> MultiSet<A> pnl(MultiSet<A> ms) {
    pnl(ms == null ? null : ms.asMap());
    return ms;
  }

  static public <A, B> MultiMap<A, B> pnl(MultiMap<A, B> mm) {
    pnl(mm == null ? null : mm.asMap());
    return mm;
  }

  static public String nlLogic_text(Exp e) {
    return e == null ? null : e.text();
  }

  static public String nlLogic_text(IfThen r) {
    return r == null ? null : r.text();
  }

  static public java.util.Timer doLater(long delay, final Object r) {
    ping();
    final java.util.Timer timer = new java.util.Timer();
    timer.schedule(timerTask(r, timer), delay);
    return vmBus_timerStarted(timer);
  }

  static public java.util.Timer doLater(double delaySeconds, final Object r) {
    return doLater(toMS(delaySeconds), r);
  }

  static public Timestamp sysTimeToTimestamp(long now) {
    return now == 0 ? null : new Timestamp(now - clockToSysTimeDiff());
  }

  static public Timestamp tsNow() {
    return new Timestamp();
  }

  static public int seconds() {
    return seconds(java.util.Calendar.getInstance());
  }

  static public int seconds(java.util.Calendar c) {
    return c.get(java.util.Calendar.SECOND);
  }

  static public boolean eqOneOf(Object o, Object... l) {
    if (l != null)
      for (Object x : l) if (eq(o, x))
        return true;
    return false;
  }

  static public Object invokeConstructor(Constructor m, Object... args) {
    try {
      makeAccessible(m);
      return m.newInstance(args);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Constructor findConstructor_precise_onTypes(Class c, Class... argTypes) {
    try {
      List<Constructor> ctors = constructorsWithNumberOfArguments(c, l(argTypes));
      Lowest<Constructor> best = new Lowest();
      if (ctors != null)
        for (Constructor ctor : ctors) {
          int score = methodApplicabilityScore_onTypes(ctor, argTypes);
          if (score < Integer.MAX_VALUE)
            best.put(ctor, score);
        }
      return best.get();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public <A> A assertEquals(Object x, A y) {
    return assertEquals("", x, y);
  }

  static public <A> A assertEquals(String msg, Object x, A y) {
    if (assertVerbose())
      return assertEqualsVerbose(msg, x, y);
    if (!(x == null ? y == null : x.equals(y)))
      throw fail((msg != null ? msg + ": " : "") + y + " != " + x);
    return y;
  }

  static public void assertEquals(Scorer scorer, Object x, Object y) {
    assertEquals(scorer, "", x, y);
  }

  static public void assertEquals(Scorer scorer, String msg, Object x, Object y) {
    if (scorer == null) {
      assertEquals(msg, x, y);
      return;
    }
    scorer.add(eq(x, y), nullIfEmpty(msg));
  }

  static public Method findNonStaticMethod_precise_onTypes(Object o, String method, Class... argTypes) {
    try {
      Class c = _getClass(o);
      if (c == null)
        return null;
      _MethodCache cache = callOpt_getCache(c);
      List<Method> methods = cache.cache.get(method);
      Lowest<Method> best = new Lowest();
      if (methods != null)
        for (Method m : methods) {
          {
            if (isStaticMethod(m))
              continue;
          }
          int score = methodApplicabilityScore_onTypes(m, argTypes);
          if (score < Integer.MAX_VALUE)
            best.put(m, score);
        }
      return best.get();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Method mostApplicableMethod_onTypes(Iterable<Method> methods, Class... argTypes) {
    Lowest<Method> best = new Lowest();
    if (methods != null)
      for (Method m : methods) {
        int score = methodApplicabilityScore_onTypes(m, argTypes);
        if (score < Integer.MAX_VALUE)
          best.put(m, score);
      }
    return best.get();
  }

  static public <A> List<A> filter(Iterable<A> c, Object pred) {
    if (pred instanceof F1)
      return filter(c, (F1<A, Boolean>) pred);
    List x = new ArrayList();
    if (c != null)
      for (Object o : c) if (isTrue(callF(pred, o)))
        x.add(o);
    return x;
  }

  static public List filter(Object pred, Iterable c) {
    return filter(c, pred);
  }

  static public <A, B extends A> List<B> filter(Iterable<B> c, F1<A, Boolean> pred) {
    List x = new ArrayList();
    if (c != null)
      for (B o : c) if (pred.get(o))
        x.add(o);
    return x;
  }

  static public <A, B extends A> List<B> filter(F1<A, Boolean> pred, Iterable<B> c) {
    return filter(c, pred);
  }

  static public <A, B extends A> List<B> filter(Iterable<B> c, IF1<A, Boolean> pred) {
    List x = new ArrayList();
    if (c != null)
      for (B o : c) if (pred.get(o))
        x.add(o);
    return x;
  }

  static public <A, B extends A> List<B> filter(B[] c, IF1<A, Boolean> pred) {
    List x = new ArrayList();
    if (c != null)
      for (B o : c) if (pred.get(o))
        x.add(o);
    return x;
  }

  static public <A, B extends A> List<B> filter(IF1<A, Boolean> pred, Iterable<B> c) {
    return filter(c, pred);
  }

  static public Map<Class, Method[]> nonDefaultInterfaceMethods_cache = newDangerousWeakHashMap();

  static public Method[] nonDefaultInterfaceMethods(Class c) {
    Method[] methods;
    synchronized (nonDefaultInterfaceMethods_cache) {
      methods = nonDefaultInterfaceMethods_cache.get(c);
      if (methods == null)
        nonDefaultInterfaceMethods_cache.put(c, methods = toTypedArray(Method.class, findNonDefaultInterfaceMethods(c)));
    }
    return methods;
  }

  static public Method findMethod_precise_onTypes(Object o, String method, Class... argTypes) {
    try {
      if (o instanceof Class) {
        _MethodCache cache = callOpt_getCache((Class) o);
        List<Method> methods = cache.cache.get(method);
        Lowest<Method> best = new Lowest();
        if (methods != null)
          for (Method m : methods) {
            {
              if (!(isStaticMethod(m)))
                continue;
            }
            int score = methodApplicabilityScore_onTypes(m, argTypes);
            if (score < Integer.MAX_VALUE)
              best.put(m, score);
          }
        return best.get();
      }
      if (o == null)
        return null;
      _MethodCache cache = callOpt_getCache(o.getClass());
      List<Method> methods = cache.cache.get(method);
      Lowest<Method> best = new Lowest();
      if (methods != null)
        for (Method m : methods) {
          int score = methodApplicabilityScore_onTypes(m, argTypes);
          if (score < Integer.MAX_VALUE)
            best.put(m, score);
        }
      return best.get();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public String fileName(File f) {
    return f == null ? null : f.getName();
  }

  static public AtomicLong randomClassName_counter = new AtomicLong();

  static public String randomClassName() {
    return "UserCode" + inc(randomClassName_counter);
  }

  static public <A> A print_tabToSingleSpace(A o) {
    print(tabToSingleSpace(str(o)));
    return o;
  }

  static public Object load(String varName) {
    readLocally(varName);
    return get(mc(), varName);
  }

  static public Object load(String progID, String varName) {
    readLocally(progID, varName);
    return get(mc(), varName);
  }

  static public ClassLoader myClassLoader() {
    return _getClass(mc()).getClassLoader();
  }

  static public <A> A newInstance(Class<A> c, Object... args) {
    return nuObject(c, args);
  }

  static public Object newInstance(String className, Object... args) {
    return nuObject(className, args);
  }

  static public <A, B> MultiSetMap<A, B> treeMultiSetMap() {
    return new MultiSetMap(true);
  }

  static public <A, B> MultiSetMap<A, B> treeMultiSetMap(Comparator<A> comparator) {
    return new MultiSetMap(new TreeMap<A, Set<B>>(comparator));
  }

  static public <A, B> B firstValue(Map<A, B> map) {
    return first(values(map));
  }

  static public <A, B> B firstValue(MultiSetMap<A, B> map) {
    return map == null ? null : first(firstValue(map.data));
  }

  static public <A, B> B firstValue(MultiMap<A, B> map) {
    return map == null ? null : first(firstValue(map.data));
  }

  static public <A, B> A firstKey(Map<A, B> map) {
    return first(keys(map));
  }

  static public <A, B> A firstKey(IMultiMap<A, B> map) {
    return map == null ? null : first(map.keySet());
  }

  static public boolean cleanUp_interruptThreads = false;

  static public void cleanUp(Object c) {
    if (c == null)
      return;
    if (c instanceof AutoCloseable) {
      close_pcall((AutoCloseable) c);
      return;
    }
    if (c instanceof java.util.Timer) {
      ((java.util.Timer) c).cancel();
      return;
    }
    if (c instanceof Collection) {
      cleanUp((Collection) c);
      return;
    }
    if (c instanceof Map) {
      for (Object o : keysList((Map) c)) cleanUp(o);
      for (Object o : valuesList((Map) c)) cleanUp(o);
      syncClear((Map) c);
      return;
    }
    try {
      preCleanUp(c);
      setOpt_raw(c, "ping_pauseAll", false);
      innerCleanUp(c);
      List androids = (List) getOpt(c, "record_list");
      for (Object android : unnull(androids)) pcallOpt(android, "dispose");
      List<WeakReference> classes = (List<WeakReference>) (getOpt(c, "hotwire_classes"));
      if (classes != null)
        for (WeakReference cc : classes) {
          try {
            cleanUp(cc.get());
          } catch (Throwable __e) {
            pcallFail(__e);
          }
        }
      if (cleanUp_interruptThreads) {
        List<Thread> threads = registeredThreads(c);
        if (nempty(threads)) {
          print("cleanUp: Interrupting " + n2(threads, "thread") + ": " + joinWithComma(allToString(threads)));
          interruptThreads(threads);
        }
      }
    } catch (Throwable __e) {
      pcallFail(__e);
    }
    setOpt_raw(c, "cleaningUp_flag", false);
    if (c instanceof Class && ((Class) c).getName().equals("main"))
      retireClassLoader(((Class) c).getClassLoader());
  }

  static public void cleanUp(Collection l) {
    if (l == null)
      return;
    for (Object c : l) cleanUp(c);
    l.clear();
  }

  static public <A extends Concept> List<A> list(Class<A> type) {
    return list(type, db_mainConcepts());
  }

  static public <A extends Concept> List<A> list(Class<A> type, Concepts cc) {
    return cc.list(type);
  }

  static public <A extends Concept> List<A> list(Concepts concepts, Class<A> type) {
    return concepts.list(type);
  }

  static public List<Concept> list(String type) {
    return db_mainConcepts().list(type);
  }

  static public List<Concept> list(Concepts concepts, String type) {
    return concepts.list(type);
  }

  static public List<Concept> list(Concepts concepts) {
    return asList(concepts.allConcepts());
  }

  static public Set emptySet() {
    return new HashSet();
  }

  static public <A, B> Map.Entry<A, B> firstEntry(Map<A, B> map) {
    return empty(map) ? null : first(map.entrySet());
  }

  static public List syncMap(Object f, Map map) {
    return syncMap(map, f);
  }

  static public List syncMap(Map map, Object f) {
    return map(cloneLinkedHashMap(map), f);
  }

  static public <A, B> Map<A, B> syncMap() {
    return synchroHashMap();
  }

  static public <A, B> Map<A, B> syncMap(Map map) {
    return synchronizedMap(map);
  }

  static public boolean stdEq2(Object a, Object b) {
    if (a == null)
      return b == null;
    if (b == null)
      return false;
    if (a.getClass() != b.getClass())
      return false;
    for (String field : allFields(a)) if (neq(getOpt(a, field), getOpt(b, field)))
      return false;
    return true;
  }

  static public int stdHash2(Object a) {
    if (a == null)
      return 0;
    return stdHash(a, toStringArray(allFields(a)));
  }

  static public long sysNow() {
    ping();
    return System.nanoTime() / 1000000;
  }

  static public Thread startThread(Object runnable) {
    return startThread(defaultThreadName(), runnable);
  }

  static public Thread startThread(String name, Runnable runnable) {
    runnable = wrapAsActivity(runnable);
    return startThread(newThread(runnable, name));
  }

  static public Thread startThread(String name, Object runnable) {
    runnable = wrapAsActivity(runnable);
    return startThread(newThread(toRunnable(runnable), name));
  }

  static public Thread startThread(Thread t) {
    _registerThread(t);
    t.start();
    return t;
  }

  static public java.util.Timer doLater_daemon(long delay, final Object r) {
    final java.util.Timer timer = new java.util.Timer(true);
    timer.schedule(timerTask(r, timer), delay);
    return timer;
  }

  static public java.util.Timer doLater_daemon(double delaySeconds, final Object r) {
    return doLater_daemon(toMS(delaySeconds), r);
  }

  static public void cancelTimer(javax.swing.Timer timer) {
    if (timer != null)
      timer.stop();
  }

  static public void cancelTimer(java.util.Timer timer) {
    if (timer != null)
      timer.cancel();
  }

  static public void cancelTimer(Object o) {
    if (o instanceof java.util.Timer)
      cancelTimer((java.util.Timer) o);
    else if (o instanceof javax.swing.Timer)
      cancelTimer((javax.swing.Timer) o);
    else if (o instanceof AutoCloseable) {
      try {
        ((AutoCloseable) o).close();
      } catch (Throwable __e) {
        pcallFail(__e);
      }
    }
  }

  static public boolean canCallWithVarargs(Object o, String method, Object... args) {
    if (o == null)
      return false;
    if (o instanceof Class) {
      Class c = (Class) o;
      _MethodCache cache = callOpt_getCache(c);
      if (cache.findStaticMethod(method, args) != null)
        return true;
      List<Method> methods = cache.cache.get(method);
      if (methods != null)
        methodSearch: for (Method m : methods) {
          {
            if (!(m.isVarArgs() && isStaticMethod(m)))
              continue;
          }
          if (massageArgsForVarArgsCall(m, args) != null)
            return true;
        }
    } else {
      Class c = o.getClass();
      _MethodCache cache = callOpt_getCache(c);
      if (cache.findMethod(method, args) != null)
        return true;
      List<Method> methods = cache.cache.get(method);
      if (methods != null)
        methodSearch: for (Method m : methods) {
          {
            if (!(m.isVarArgs()))
              continue;
          }
          if (massageArgsForVarArgsCall(m, args) != null)
            return true;
        }
    }
    return false;
  }

  static public void smartSet(Field f, Object o, Object value) throws Exception {
    try {
      f.set(o, value);
    } catch (Exception e) {
      Class type = f.getType();
      if (type == int.class && value instanceof Long) {
        f.set(o, ((Long) value).intValue());
        return;
      }
      if (type == boolean.class && value instanceof String) {
        f.set(o, isTrueOrYes(((String) value)));
        return;
      }
      if (type == LinkedHashMap.class && value instanceof Map) {
        f.set(o, asLinkedHashMap((Map) value));
        return;
      }
      try {
        if (f.getType() == Concept.Ref.class) {
          f.set(o, ((Concept) o).new Ref((Concept) value));
          return;
        }
        if (o instanceof Concept.Ref) {
          f.set(o, ((Concept.Ref) o).get());
          return;
        }
      } catch (Throwable _e) {
      }
      throw e;
    }
  }

  static public String unquoteSingleOrDoubleQuotes(String s) {
    if (s == null)
      return null;
    if (s.length() > 1) {
      char c = s.charAt(0);
      if (c == '\"' || c == '\'') {
        int l = endsWith(s, c) ? s.length() - 1 : s.length();
        StringBuilder sb = new StringBuilder(l - 1);
        for (int i = 1; i < l; i++) {
          char ch = s.charAt(i);
          if (ch == '\\') {
            char nextChar = (i == l - 1) ? '\\' : s.charAt(i + 1);
            if (nextChar >= '0' && nextChar <= '7') {
              String code = "" + nextChar;
              i++;
              if ((i < l - 1) && s.charAt(i + 1) >= '0' && s.charAt(i + 1) <= '7') {
                code += s.charAt(i + 1);
                i++;
                if ((i < l - 1) && s.charAt(i + 1) >= '0' && s.charAt(i + 1) <= '7') {
                  code += s.charAt(i + 1);
                  i++;
                }
              }
              sb.append((char) Integer.parseInt(code, 8));
              continue;
            }
            switch(nextChar) {
              case '\"':
                ch = '\"';
                break;
              case '\\':
                ch = '\\';
                break;
              case 'b':
                ch = '\b';
                break;
              case 'f':
                ch = '\f';
                break;
              case 'n':
                ch = '\n';
                break;
              case 'r':
                ch = '\r';
                break;
              case 't':
                ch = '\t';
                break;
              case '\'':
                ch = '\'';
                break;
              case 'u':
                if (i >= l - 5) {
                  ch = 'u';
                  break;
                }
                int code = Integer.parseInt("" + s.charAt(i + 2) + s.charAt(i + 3) + s.charAt(i + 4) + s.charAt(i + 5), 16);
                sb.append(Character.toChars(code));
                i += 5;
                continue;
              default:
                ch = nextChar;
            }
            i++;
          }
          sb.append(ch);
        }
        return sb.toString();
      }
    }
    return s;
  }

  static public void addToContainer(Container a, Component... b) {
    if (a == null)
      return;
    {
      swing(() -> {
        for (Component c : unnullForIteration(b)) if (c != null)
          a.add(c);
      });
    }
  }

  static public String strOrNull(Object o) {
    return o == null ? null : str(o);
  }

  static public String rep(int n, char c) {
    return repeat(c, n);
  }

  static public String rep(char c, int n) {
    return repeat(c, n);
  }

  static public <A> List<A> rep(A a, int n) {
    return repeat(a, n);
  }

  static public <A> List<A> rep(int n, A a) {
    return repeat(n, a);
  }

  static public String decimalFormatEnglish(String format, double d) {
    return decimalFormatEnglish(format).format(d);
  }

  static public java.text.DecimalFormat decimalFormatEnglish(String format) {
    return new java.text.DecimalFormat(format, new java.text.DecimalFormatSymbols(Locale.ENGLISH));
  }

  static public <A extends Container> A addComponents(A c, Collection<? extends Component> components) {
    if (nempty(components)) {
      swing(() -> {
        for (Component comp : components) if (comp != null)
          c.add(comp);
        revalidate(c);
      });
    }
    return c;
  }

  static public <A extends Container> A addComponents(A c, Component... components) {
    return addComponents(c, asList(components));
  }

  static public String localDateWithSeconds(long time) {
    SimpleDateFormat format = simpleDateFormat_local("yyyy/MM/dd HH:mm:ss");
    return format.format(time);
  }

  static public String localDateWithSeconds() {
    return localDateWithSeconds(now());
  }

  static public long clockToSysTimeDiff() {
    return sysNow() - now();
  }

  static public TreeSet<String> caseInsensitiveSet() {
    return caseInsensitiveSet_treeSet();
  }

  static public TreeSet<String> caseInsensitiveSet(Collection<String> c) {
    return caseInsensitiveSet_treeSet(c);
  }

  static public <A> void replaceLastElement(List<A> l, A a) {
    if (nempty(l))
      l.set(l(l) - 1, a);
  }

  static public <A> String pnlToString(String prefix, Iterable<A> l) {
    return hijackPrint(new Runnable() {

      public void run() {
        try {
          pnl(prefix, l);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "pnl(prefix, l)";
      }
    });
  }

  static public <A> String pnlToString(final Iterable<A> l) {
    return hijackPrint(new Runnable() {

      public void run() {
        try {
          pnl(l);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "pnl(l)";
      }
    });
  }

  static public <A> String pnlToString(final A[] l) {
    return hijackPrint(new Runnable() {

      public void run() {
        try {
          pnl(l);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "pnl(l)";
      }
    });
  }

  static public <A, B> String pnlToString(final Map<A, B> map) {
    return hijackPrint(new Runnable() {

      public void run() {
        try {
          pnl(map);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "pnl(map)";
      }
    });
  }

  static public <A, B> String pnlToString(MultiMap<A, B> map) {
    return pnlToString(multiMapToMap(map));
  }

  static public String renderRecordVars(String recordName, Object... params) {
    List<String> l = new ArrayList();
    int i = 0;
    for (; i + 1 < l(params); i += 2) l.add(params[i] + "=" + params[i + 1]);
    return formatFunctionCall(recordName, l);
  }

  static public String dropSuffix(String suffix, String s) {
    return nempty(suffix) && endsWith(s, suffix) ? s.substring(0, l(s) - l(suffix)) : s;
  }

  static public String repeat(char c, int n) {
    n = Math.max(n, 0);
    char[] chars = new char[n];
    for (int i = 0; i < n; i++) chars[i] = c;
    return new String(chars);
  }

  static public <A> List<A> repeat(A a, int n) {
    n = Math.max(n, 0);
    List<A> l = new ArrayList(n);
    for (int i = 0; i < n; i++) l.add(a);
    return l;
  }

  static public <A> List<A> repeat(int n, A a) {
    return repeat(a, n);
  }

  static public int indent_default = 2;

  static public String indent(int indent) {
    return repeat(' ', indent);
  }

  static public String indent(int indent, String s) {
    return indent(repeat(' ', indent), s);
  }

  static public String indent(String indent, String s) {
    return indent + replace(unnull(s), "\n", "\n" + indent);
  }

  static public String indent(String s) {
    return indent(indent_default, s);
  }

  static public List<String> indent(String indent, List<String> lines) {
    List<String> l = new ArrayList();
    if (lines != null)
      for (String s : lines) l.add(indent + s);
    return l;
  }

  static public String jreplace_dyn(String s, String in, Object out) {
    return jreplace_dyn(s, in, out, null);
  }

  static public String jreplace_dyn(String s, String in, Object out, Object condition) {
    List<String> tok = javaTok(s);
    jreplace_dyn(tok, in, out, condition);
    return join(tok);
  }

  static public boolean jreplace_dyn(List<String> tok, String in, Object out) {
    return jreplace_dyn(tok, in, out, false, true, null);
  }

  static public boolean jreplace_dyn(List<String> tok, String in, IF0<String> out) {
    return jreplace_dyn(tok, in, if0ToIF2(out));
  }

  static public boolean jreplace_dyn(List<String> tok, String in, ITokCondition cond, IF0<String> out) {
    return jreplace_dyn(tok, in, cond, if0ToIF2(out));
  }

  static public boolean jreplace_dyn(List<String> tok, String in, IF1<Integer, String> out) {
    return jreplace_dyn(tok, in, (_tok, cIdx) -> out.get(cIdx));
  }

  static public boolean jreplace_dyn(List<String> tok, String in, IF2<List<String>, Integer, String> out) {
    return jreplace_dyn(tok, in, (Object) out);
  }

  static public boolean jreplace_dyn(List<String> tok, String in, IF2<List<String>, Integer, String> out, IF2<List<String>, Integer, Boolean> condition) {
    return jreplace_dyn(tok, in, (Object) out, (Object) condition);
  }

  static public boolean jreplace_dyn(List<String> tok, String in, Object out, Object condition) {
    return jreplace_dyn(tok, in, out, false, true, condition);
  }

  static public boolean jreplace_dyn(List<String> tok, String in, Object out, boolean ignoreCase, boolean reTok, Object condition) {
    List<String> tokin = javaTok(in);
    jfind_preprocess(tokin);
    String[] toks = toStringArray(codeTokensOnly(tokin));
    boolean anyChange = false;
    for (int n = 0; n < 10000; n++) {
      int i = findCodeTokens(tok, 1, ignoreCase, toks, condition);
      if (i < 0)
        return anyChange;
      String expansion = (String) (callF(out, tok, i));
      int end = i + l(tokin) - 2;
      clearAllTokens(tok, i, end);
      tok.set(i, unnull(expansion));
      if (reTok)
        reTok(tok, i, end);
      anyChange = true;
    }
    throw fail("woot? 10000! " + quote(in) + " => " + quote(out));
  }

  static public int tok_conditionals(List<String> tok, String keyword, String keywordEnd, IF1<String, Boolean> pred, boolean reTokImmediately, boolean noReTok) {
    int changes = 0;
    if (tok instanceof IContentsIndexedList) {
      int[] l = ((IContentsIndexedList) tok).indicesOf(keyword);
      for (int j = l(l) - 1; j >= 0; j--) {
        int i = l[j];
        if (isIdentifier(get(tok, i + 2))) {
          tok_conditionals_processConditional(tok, i, keyword, keywordEnd, pred, reTokImmediately && !noReTok);
          ++changes;
        }
      }
    } else {
      if (!tok.contains(keyword))
        return changes;
      int i = l(tok);
      while ((i = rjfind(tok, 1, i - 1, keyword + " <id>")) >= 0) {
        ++changes;
        tok_conditionals_processConditional(tok, i, keyword, keywordEnd, pred, reTokImmediately && !noReTok);
      }
    }
    if (changes != 0 && !reTokImmediately && !noReTok)
      reTok(tok);
    return changes;
  }

  static public void tok_conditionals_processConditional(List<String> tok, int i, String keyword, String keywordEnd, IF1<String, Boolean> pred, boolean reTokImmediately) {
    int j = jfind(tok, i + 4, keywordEnd);
    if (j < 0)
      j = l(tok) - 1;
    String name = tok.get(i + 2);
    boolean has = pred.get(name);
    if (has) {
      clearTokens_maybeReTok(tok, j, j + 1, reTokImmediately);
      clearTokens_maybeReTok(tok, i, i + 3, reTokImmediately);
    } else
      clearTokens_maybeReTok(tok, i, j + 1, reTokImmediately);
  }

  static public List<String> replaceKeywordBlock(List<String> tok, String keyword, String beg, String end) {
    return replaceKeywordBlock(tok, keyword, beg, end, false, null);
  }

  static public List<String> replaceKeywordBlock(List<String> tok, String keyword, String beg, String end, Object cond) {
    return replaceKeywordBlock(tok, keyword, beg, end, false, cond);
  }

  static public List<String> replaceKeywordBlock(List<String> tok, String keyword, String beg, String end, boolean debug, Object cond) {
    for (int n = 0; n < 1000; n++) {
      int i = jfind(tok, keyword + " {", cond);
      if (i < 0)
        break;
      int idx = findCodeTokens(tok, i, false, "{");
      int j = findEndOfBracketPart(tok, idx);
      if (debug) {
        print(toUpper(keyword) + " BEFORE\n" + join(subList(tok, i, j)));
        print("  THEN " + join(subList(tok, j, j + 10)));
      }
      List<String> subList = subList(tok, i - 1, idx);
      tok.set(j - 1, jreplaceExpandRefs(end, subList));
      replaceTokens(tok, i, idx + 1, jreplaceExpandRefs(beg, subList));
      reTok(tok, i, j);
      if (debug)
        print(toUpper(keyword) + "\n" + join(subList(tok, i, j)) + "\n");
    }
    return tok;
  }

  static public int jfind(String s, String in) {
    return jfind(javaTok(s), in);
  }

  static public int jfind(List<String> tok, String in) {
    return jfind(tok, 1, in);
  }

  static public int jfind(List<String> tok, int startIdx, String in) {
    return jfind(tok, startIdx, in, (ITokCondition) null);
  }

  static public int jfind(List<String> tok, String in, Object condition) {
    return jfind(tok, 1, in, condition);
  }

  static public int jfind(List<String> tok, String in, IIntPred condition) {
    return jfind(tok, 1, in, condition);
  }

  static public int jfind(List<String> tok, int startIndex, String in, IIntPred condition) {
    return jfind(tok, startIndex, in, tokCondition(condition));
  }

  static public int jfind(List<String> tok, String in, ITokCondition condition) {
    return jfind(tok, 1, in, condition);
  }

  static public int jfind(List<String> tok, int startIndex, String in, ITokCondition condition) {
    return jfind(tok, startIndex, in, (Object) condition);
  }

  static public int jfind(List<String> tok, int startIdx, String in, Object condition) {
    return jfind(tok, startIdx, javaTokForJFind_array(in), condition);
  }

  static public int jfind(List<String> tok, List<String> tokin) {
    return jfind(tok, 1, tokin);
  }

  static public int jfind(List<String> tok, int startIdx, List<String> tokin) {
    return jfind(tok, startIdx, tokin, null);
  }

  static public int jfind(List<String> tok, int startIdx, String[] tokinC, Object condition) {
    return findCodeTokens(tok, startIdx, false, tokinC, condition);
  }

  static public int jfind(List<String> tok, int startIdx, List<String> tokin, Object condition) {
    return jfind(tok, startIdx, codeTokensAsStringArray(tokin), condition);
  }

  static public List<String> jfind_preprocess(List<String> tok) {
    for (String type : litlist("quoted", "id", "int")) replaceSublist(tok, ll("<", "", type, "", ">"), ll("<" + type + ">"));
    replaceSublist(tok, ll("\\", "", "*"), ll("\\*"));
    return tok;
  }

  static public String tok_script_scanType(List<String> tok, int iTypeStart) {
    int iEndOfType = tok_script_findEndOfType(tok, iTypeStart);
    return joinSubList_cToC(tok, iTypeStart, iEndOfType);
  }

  static public void replaceToken_reTok(List<String> tok, int i, String t) {
    tokSet_withReTok(tok, i, t);
  }

  static public Map<String, String[]> javaTokForJFind_array_cache = synchronizedMRUCache(1000);

  static public String[] javaTokForJFind_array(String s) {
    String[] tok = javaTokForJFind_array_cache.get(s);
    if (tok == null)
      javaTokForJFind_array_cache.put(s, tok = codeTokensAsStringArray(jfind_preprocess(javaTok(s))));
    return tok;
  }

  static public int findCodeTokens(List<String> tok, String... tokens) {
    return findCodeTokens(tok, 1, false, tokens);
  }

  static public int findCodeTokens(List<String> tok, boolean ignoreCase, String... tokens) {
    return findCodeTokens(tok, 1, ignoreCase, tokens);
  }

  static public int findCodeTokens(List<String> tok, int startIdx, boolean ignoreCase, String... tokens) {
    return findCodeTokens(tok, startIdx, ignoreCase, tokens, null);
  }

  static public HashSet<String> findCodeTokens_specials = lithashset("*", "<quoted>", "<id>", "<int>", "\\*");

  static public int findCodeTokens_bails, findCodeTokens_nonbails;

  static public interface findCodeTokens_Matcher {

    public boolean get(String token);
  }

  static public int findCodeTokens(List<String> tok, int startIdx, boolean ignoreCase, String[] tokens, Object condition) {
    int end = tok.size() - tokens.length * 2 + 2, nTokens = tokens.length;
    int i = startIdx | 1;
    findCodeTokens_Matcher[] matchers = new findCodeTokens_Matcher[nTokens];
    IContentsIndexedList2 indexedList = tok instanceof IContentsIndexedList2 ? (IContentsIndexedList2) tok : null;
    TreeSet<HasIndex> indices = null;
    int indicesOfs = 0;
    for (int j = 0; j < nTokens; j++) {
      String p = tokens[j];
      findCodeTokens_Matcher matcher;
      if (p.equals("*"))
        matcher = t -> true;
      else if (p.equals("<quoted>"))
        matcher = t -> isQuoted(t);
      else if (p.equals("<id>"))
        matcher = t -> isIdentifier(t);
      else if (p.equals("<int>"))
        matcher = t -> isInteger(t);
      else if (p.equals("\\*"))
        matcher = t -> t.equals("*");
      else if (ignoreCase)
        matcher = t -> eqic(p, t);
      else {
        matcher = t -> t.equals(p);
        if (indexedList != null) {
          TreeSet<HasIndex> indices2 = indexedList.indicesOf_treeSetOfHasIndex(p);
          if (indices2 == null)
            return -1;
          if (indices == null || indices2.size() < indices.size()) {
            indices = indices2;
            indicesOfs = j;
          }
        }
      }
      matchers[j] = matcher;
    }
    if (indices != null) {
      int min = i + indicesOfs * 2;
      SortedSet<HasIndex> tailSet = min == 1 ? indices : indices.tailSet(new HasIndex(min));
      outer: for (HasIndex h : tailSet) {
        int idx = h.idx - indicesOfs * 2;
        if (idx >= end)
          break;
        for (int j = 0; j < nTokens; j++) try {
          if (!matchers[j].get(tok.get(idx + j * 2)))
            continue outer;
        } catch (IndexOutOfBoundsException e) {
          print("fct indicesOfs=" + indicesOfs + ", h=" + h + ", idx=" + idx);
          throw e;
        }
        if (condition == null || checkTokCondition(condition, tok, idx - 1))
          return idx;
      }
      return -1;
    }
    outer: for (; i < end; i += 2) {
      for (int j = 0; j < nTokens; j++) if (!matchers[j].get(tok.get(i + j * 2)))
        continue outer;
      if (condition == null || checkTokCondition(condition, tok, i - 1))
        return i;
    }
    return -1;
  }

  static public String jreplaceExpandRefs(String s, List<String> tokref) {
    if (!contains(s, '$'))
      return s;
    List<String> tok = javaTok(s);
    for (int i = 1; i < l(tok); i += 2) {
      String t = tok.get(i);
      if (t.startsWith("$") && isInteger(t.substring(1))) {
        String x = tokref.get(-1 + parseInt(t.substring(1)) * 2);
        tok.set(i, x);
      } else if (t.equals("\\")) {
        tok.set(i, "");
        i += 2;
      }
    }
    return join(tok);
  }

  static public void clearAllTokens(List<String> tok) {
    for (int i = 0; i < tok.size(); i++) tok.set(i, "");
  }

  static public void clearAllTokens(List<String> tok, int i, int j) {
    for (; i < j; i++) tok.set(i, "");
  }

  static public List<String> reTok(List<String> tok) {
    replaceCollection(tok, javaTok(tok));
    return tok;
  }

  static public List<String> reTok(List<String> tok, int i) {
    return reTok(tok, i, i + 1);
  }

  static public List<String> reTok(List<String> tok, int i, int j) {
    i = max(i & ~1, 0);
    j = min(l(tok), j | 1);
    if (i >= j)
      return tok;
    List<String> t = javaTok(joinSubList(tok, i, j));
    replaceListPart(tok, i, j, t);
    return tok;
  }

  static public List<String> reTok(List<String> tok, IntRange r) {
    if (r != null)
      reTok(tok, r.start, r.end);
    return tok;
  }

  static public String strUnnull(Object o) {
    return o == null ? "" : str(o);
  }

  static public String jlabel_textAsHTML_center_ifNeeded(String text) {
    if (swic(text, "<html>") && ewic(text, "</html>"))
      return text;
    if (!containsNewLines(text))
      return text;
    return jlabel_textAsHTML_center(text);
  }

  static public String joinNemptiesWithSpace(String... strings) {
    return joinNempties(" ", strings);
  }

  static public String joinNemptiesWithSpace(Collection<String> strings) {
    return joinNempties(" ", strings);
  }

  static public List flattenCollections(Iterable a) {
    List l = new ArrayList();
    for (Object x : a) if (x instanceof Collection)
      l.addAll(flattenCollections((Collection) x));
    else
      l.add(x);
    return l;
  }

  static public <A, B> void copyMap(Map<? extends A, ? extends B> a, Map<A, B> b) {
    if (a == null || b == null)
      return;
    b.clear();
    b.putAll(a);
  }

  static public String assertIsIdentifier(String s) {
    if (!isIdentifier(s))
      throw fail("Not an identifier: " + quote(s));
    return s;
  }

  static public String assertIsIdentifier(String msg, String s) {
    if (!isIdentifier(s))
      throw fail(msg + " - Not an identifier: " + quote(s));
    return s;
  }

  static public boolean isJavaIdentifier(String s) {
    if (empty(s) || !Character.isJavaIdentifierStart(s.charAt(0)))
      return false;
    for (int i = 1; i < s.length(); i++) if (!Character.isJavaIdentifierPart(s.charAt(i)))
      return false;
    return true;
  }

  static public <A> A optCast(Class<A> c, Object o) {
    return isInstance(c, o) ? (A) o : null;
  }

  static public void printVars_str(Object... params) {
    print(renderVars_str(params));
  }

  static public void printShortenedFunctionCall(Object fname, Object... args) {
    printShortenedFunctionCall(100, fname, args);
  }

  static public void printShortenedFunctionCall(int len, Object fname, Object... args) {
    print(shorten(len, formatFunctionCall(fname, args)));
  }

  static public boolean isNormalQuoted(String s) {
    int l = l(s);
    if (!(l >= 2 && s.charAt(0) == '"' && lastChar(s) == '"'))
      return false;
    int j = 1;
    while (j < l) if (s.charAt(j) == '"')
      return j == l - 1;
    else if (s.charAt(j) == '\\' && j + 1 < l)
      j += 2;
    else
      ++j;
    return false;
  }

  static public boolean isMultilineQuoted(String s) {
    if (!startsWith(s, "["))
      return false;
    int i = 1, n = s.length();
    while (i < n && s.charAt(i) == '=') ++i;
    return i < n && s.charAt(i) == '[';
  }

  static public boolean startsWithIgnoreCase(String a, String b) {
    return regionMatchesIC(a, 0, b, 0, b.length());
  }

  static public int hexToInt(String s) {
    return Integer.parseInt(s, 16);
  }

  static public boolean longIsInt(long l) {
    return l == (int) l;
  }

  static public char lastChar(String s) {
    return empty(s) ? '\0' : s.charAt(l(s) - 1);
  }

  static public <A> A[] dropLast(A[] a) {
    return dropLast(a, 1);
  }

  static public <A> A[] dropLast(A[] a, int n) {
    if (a == null)
      return null;
    n = Math.min(n, a.length);
    A[] b = arrayOfSameType(a, a.length - n);
    System.arraycopy(a, 0, b, 0, b.length);
    return b;
  }

  static public <A> List<A> dropLast(List<A> l) {
    return subList(l, 0, l(l) - 1);
  }

  static public <A> List<A> dropLast(int n, List<A> l) {
    return subList(l, 0, l(l) - n);
  }

  static public <A> List<A> dropLast(Iterable<A> l) {
    return dropLast(asList(l));
  }

  static public String dropLast(String s) {
    return substring(s, 0, l(s) - 1);
  }

  static public String dropLast(String s, int n) {
    return substring(s, 0, l(s) - n);
  }

  static public String dropLast(int n, String s) {
    return dropLast(s, n);
  }

  static public _MethodCache getMethodCache(Class c) {
    return callOpt_getCache(c);
  }

  static public Object fieldGet(Field f, Object o) {
    try {
      return f == null ? null : f.get(o);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Map<Class, Constructor[]> getDeclaredConstructors_cached_cache = newDangerousWeakHashMap();

  static public Constructor[] getDeclaredConstructors_cached(Class c) {
    Constructor[] ctors;
    synchronized (getDeclaredConstructors_cached_cache) {
      ctors = getDeclaredConstructors_cached_cache.get(c);
      if (ctors == null) {
        getDeclaredConstructors_cached_cache.put(c, ctors = c.getDeclaredConstructors());
        for (var ctor : ctors) makeAccessible(ctor);
      }
    }
    return ctors;
  }

  static public Object[] toObjectArray(Collection c) {
    return toObjectArray((Iterable) c);
  }

  static public Object[] toObjectArray(Iterable c) {
    List l = asList(c);
    return l.toArray(new Object[l.size()]);
  }

  static public <A> A[] arrayOfType(Class<A> type, int n) {
    return makeArray(type, n);
  }

  static public <A> A[] arrayOfType(int n, Class<A> type) {
    return arrayOfType(type, n);
  }

  static public <A> List<A> withoutNulls(Iterable<A> l) {
    if (l instanceof List)
      if (!containsNulls((List) l))
        return ((List) l);
    List<A> l2 = new ArrayList();
    for (A a : l) if (a != null)
      l2.add(a);
    return l2;
  }

  static public <A, B> Map<A, B> withoutNulls(Map<A, B> map) {
    Map<A, B> map2 = similarEmptyMap(map);
    for (A a : keys(map)) if (a != null) {
      B b = map.get(a);
      if (b != null)
        map2.put(a, b);
    }
    return map2;
  }

  static public <A> List<A> withoutNulls(A[] l) {
    List<A> l2 = new ArrayList();
    if (l != null)
      for (A a : l) if (a != null)
        l2.add(a);
    return l2;
  }

  static public List mapNonNulls(Iterable l, Object f) {
    return mapNonNulls(f, l);
  }

  static public List mapNonNulls(Object f, Iterable l) {
    List x = new ArrayList();
    if (l != null)
      for (Object o : l) addIfNotNull(x, callF(f, o));
    return x;
  }

  static public List mapNonNulls(Object f, Object[] l) {
    List x = new ArrayList();
    if (l != null)
      for (Object o : l) addIfNotNull(x, callF(f, o));
    return x;
  }

  static public <A, B> List<B> mapNonNulls(Iterable<A> l, F1<A, B> f) {
    return mapNonNulls(f, l);
  }

  static public <A, B> List<B> mapNonNulls(F1<A, B> f, Iterable<A> l) {
    List x = new ArrayList();
    if (l != null)
      for (Object o : l) addIfNotNull(x, callF(f, o));
    return x;
  }

  static public <A, B> List<B> mapNonNulls(A[] l, IF1<A, B> f) {
    return mapNonNulls(f, l);
  }

  static public <A, B> List<B> mapNonNulls(Iterable<A> l, IF1<A, B> f) {
    List x = emptyList(l);
    if (l != null)
      for (A o : l) addIfNotNull(x, f.get(o));
    return x;
  }

  static public <A, B> List<B> mapNonNulls(IF1<A, B> f, Iterable<A> l) {
    return mapNonNulls(l, f);
  }

  static public <A, B, C, D> Map<C, D> mapMapToMap(IF2<A, B, Pair<C, D>> f, Map<A, B> m) {
    if (m == null)
      return null;
    Map<C, D> map = similarEmptyMap(m);
    for (Map.Entry<A, B> e : m.entrySet()) {
      Pair<C, D> p = f.get(e.getKey(), e.getValue());
      map.put(p.a, p.b);
    }
    return map;
  }

  static public <A, B, C, D> Map<C, D> mapMapToMap(Map<A, B> m, IF2<A, B, Pair<C, D>> f) {
    return mapMapToMap(f, m);
  }

  static public <A> ArrayList<A> cloneListSynchronizingOn(Collection<A> l, Object mutex) {
    if (l == null)
      return new ArrayList();
    synchronized (mutex) {
      return new ArrayList<A>(l);
    }
  }

  static public <A> A[] newArray(Class<A> c, int n) {
    return typedArray(c, n);
  }

  static public <A, B> Iterator<B> valueIterator(Map<A, B> map) {
    return map == null ? emptyItIt() : map.values().iterator();
  }

  static public String longestPrefixInNavigableSet(String s, NavigableSet<String> set) {
    if (set == null || s == null)
      return null;
    while (licensed()) {
      String key = set.floor(s);
      if (key == null)
        break;
      int n = lCommonPrefix(key, s);
      if (n == l(key))
        return key;
      s = takeFirst(s, n);
    }
    return null;
  }

  static public List<String> notEndingWith(Collection<String> l, final String suffix) {
    return filter(unnull(l), new F1<String, Boolean>() {

      public Boolean get(String s) {
        try {
          return !endsWith(s, suffix);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "!endsWith(s, suffix)";
      }
    });
  }

  static public List<String> notEndingWith(String suffix, Collection<String> l) {
    return notEndingWith(l, suffix);
  }

  static public Method fastIntern_method;

  static public String fastIntern(String s) {
    try {
      if (s == null)
        return null;
      if (fastIntern_method == null) {
        fastIntern_method = findMethodNamed(javax(), "internPerProgram");
        if (fastIntern_method == null)
          upgradeJavaXAndRestart();
      }
      return (String) fastIntern_method.invoke(null, s);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public double frac_nonNeg(double d) {
    return mod(d, 1);
  }

  static public <A, B> int lKeys(MultiMap<A, B> mm) {
    return mm == null ? 0 : mm.keysSize();
  }

  static public Method findMethodNamed(Object obj, String method) {
    if (obj == null)
      return null;
    if (obj instanceof Class)
      return findMethodNamed((Class) obj, method);
    return findMethodNamed(obj.getClass(), method);
  }

  static public Method findMethodNamed(Class c, String method) {
    while (c != null) {
      for (Method m : c.getDeclaredMethods()) if (m.getName().equals(method)) {
        makeAccessible(m);
        return m;
      }
      c = c.getSuperclass();
    }
    return null;
  }

  static public int hashMap_internalHash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  }

  static public boolean checkFields(Object x, Object... data) {
    for (int i = 0; i < l(data); i += 2) if (neq(getOpt(x, (String) data[i]), data[i + 1]))
      return false;
    return true;
  }

  static public String hfulltag(String tag) {
    return hfulltag(tag, "");
  }

  static public String hfulltag(String tag, Object contents, Object... params) {
    return hopeningTag(tag, params) + str(contents) + "</" + tag + ">";
  }

  static public String fulltag(String tag) {
    return hfulltag(tag);
  }

  static public String fulltag(String tag, Object contents, Object... params) {
    return hfulltag(tag, contents, params);
  }

  static public String joinNempties(String sep, Object... strings) {
    return joinStrings(sep, strings);
  }

  static public String joinNempties(String sep, Iterable strings) {
    return joinStrings(sep, strings);
  }

  static public String joinNemptiesWithComma(Object... strings) {
    return joinNempties(", ", strings);
  }

  static public String joinNemptiesWithComma(Iterable strings) {
    return joinNempties(", ", strings);
  }

  static public List flattenCollectionsAndArrays(Iterable a) {
    List l = new ArrayList();
    for (Object x : a) if (x instanceof Collection)
      l.addAll(flattenCollectionsAndArrays((Collection) x));
    else if (x instanceof Object[])
      l.addAll(flattenCollectionsAndArrays(asList((Object[]) x)));
    else
      l.add(x);
    return l;
  }

  static public List flattenCollectionsAndArrays(Object... whatever) {
    return flattenCollectionsAndArrays(ll(whatever));
  }

  static public Color colorFromRGBA(int rgba) {
    return new Color(rgba, true);
  }

  static public int ubyteToInt(byte b) {
    return b & 0x0FF;
  }

  static public int ubyteToInt(char c) {
    return c & 0x0FF;
  }

  static public CharSequence subCharSequence(CharSequence s, int x) {
    return subCharSequence(s, x, s == null ? 0 : s.length());
  }

  static public CharSequence subCharSequence(CharSequence s, int x, int y) {
    if (s == null)
      return null;
    if (x < 0)
      x = 0;
    if (x >= s.length())
      return "";
    if (y < x)
      y = x;
    if (y > s.length())
      y = s.length();
    return s.subSequence(x, y);
  }

  static public String newLinesToSpaces_trim(String s) {
    return s == null ? null : trim(s).replaceAll("\\s*\r?\n\\s*", " ");
  }

  static public String formatWithThousands(long l) {
    return formatWithThousandsSeparator(l);
  }

  static public String plural(String s) {
    return getPlural(s);
  }

  static public double fraction(double d) {
    return d % 1;
  }

  static public String n_fancy2(long l, String singular, String plural) {
    return formatWithThousandsSeparator(l) + " " + trim(l == 1 ? singular : plural);
  }

  static public String n_fancy2(Collection l, String singular, String plural) {
    return n_fancy2(l(l), singular, plural);
  }

  static public String n_fancy2(Map m, String singular, String plural) {
    return n_fancy2(l(m), singular, plural);
  }

  static public String n_fancy2(Object[] a, String singular, String plural) {
    return n_fancy2(l(a), singular, plural);
  }

  static public String n_fancy2(MultiSet ms, String singular, String plural) {
    return n_fancy2(l(ms), singular, plural);
  }

  static public int methodApplicabilityScore_withPrimitiveWidening(Executable m, Object[] args) {
    Class<?>[] types = m.getParameterTypes();
    if (types.length != l(args))
      return Integer.MAX_VALUE;
    int score = 0;
    boolean widenings = false;
    for (int i = 0; i < types.length; i++) {
      Object a = args[i];
      Class c = types[i];
      if (a == null) {
        if (c.isPrimitive())
          return Integer.MAX_VALUE;
      } else {
        Class t = a.getClass();
        int s = typeConversionScoreWithUnboxing(t, c);
        if (s == Integer.MAX_VALUE)
          return Integer.MAX_VALUE;
        if (s < 0)
          widenings = true;
        score += abs(s);
      }
    }
    return widenings ? -score : score;
  }

  static public int abs(int i) {
    return Math.abs(i);
  }

  static public long abs(long i) {
    return Math.abs(i);
  }

  static public float abs(float i) {
    return Math.abs(i);
  }

  static public double abs(double i) {
    return Math.abs(i);
  }

  static public double abs(Complex c) {
    return c.abs();
  }

  static public Object invokeConstructorWithWidening(Constructor m, Object... args) {
    try {
      try {
        Class<?>[] types = m.getParameterTypes();
        int n = types.length;
        Object[] args2 = new Object[n];
        for (int i = 0; i < n; i++) args2[i] = convertPrimitiveIfNecessary(args[i], types[i]);
        return m.newInstance(args2);
      } catch (IllegalArgumentException e) {
        throw new IllegalArgumentException(e.getMessage() + " - was calling: " + m + ", args: " + joinWithSpace(classNames(args)));
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public List<String> allToString(Iterable c) {
    List<String> l = new ArrayList();
    for (Object o : unnull(c)) l.add(str(o));
    return l;
  }

  static public List<String> allToString(Object[] c) {
    List<String> l = new ArrayList();
    for (Object o : unnull(c)) l.add(str(o));
    return l;
  }

  static public String hostToIP(String host) {
    try {
      for (InetAddress a : InetAddress.getAllByName(host)) {
        String ip = a.getHostAddress();
        if (isIPv4(ip))
          return ip;
      }
      throw fail("No IP address found for " + host);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Object callWithVarargs(Object o, String method, Object... args) {
    return call_withVarargs(o, method, args);
  }

  static public int methodApplicabilityScore_withPrimitiveWidening_onTypes(Executable m, Class[] argTypes) {
    Class<?>[] types = m.getParameterTypes();
    if (types.length != l(argTypes))
      return Integer.MAX_VALUE;
    int score = 0;
    boolean widenings = false;
    for (int i = 0; i < types.length; i++) {
      Class t = argTypes[i];
      Class c = types[i];
      int s = typeConversionScoreWithUnboxing(t, c);
      if (s == Integer.MAX_VALUE)
        return Integer.MAX_VALUE;
      if (s < 0)
        widenings = true;
      score += abs(s);
    }
    return widenings ? -score : score;
  }

  static public Object callWithVarargs_sentinel(Object o, String methodName, Object methodNotFoundSentinel, Object... args) {
    try {
      if (o == null)
        return null;
      if (o instanceof Class) {
        Class c = (Class) o;
        _MethodCache cache = callOpt_getCache(c);
        Method me = cache.findStaticMethod(methodName, args);
        if (me != null)
          return invokeMethod(me, null, args);
        List<Method> methods = cache.cache.get(methodName);
        if (methods != null)
          methodSearch: for (Method m : methods) {
            {
              if (!(m.isVarArgs()))
                continue;
            }
            {
              if (!(isStaticMethod(m)))
                continue;
            }
            Object[] newArgs = massageArgsForVarArgsCall(m, args);
            if (newArgs != null)
              return invokeMethod(m, null, newArgs);
          }
        return methodNotFoundSentinel;
      } else {
        Class c = o.getClass();
        _MethodCache cache = callOpt_getCache(c);
        Method me = cache.findMethod(methodName, args);
        if (me != null)
          return invokeMethod(me, o, args);
        List<Method> methods = cache.cache.get(methodName);
        if (methods != null)
          methodSearch: for (Method m : methods) {
            {
              if (!(m.isVarArgs()))
                continue;
            }
            Object[] newArgs = massageArgsForVarArgsCall(m, args);
            if (newArgs != null)
              return invokeMethod(m, o, newArgs);
          }
        return methodNotFoundSentinel;
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public <A> A[] singlePlusArray(A a, A[] l) {
    A[] out = newObjectArrayOfSameType(l, l(l) + 1);
    out[0] = a;
    arraycopy(l, 0, out, 1, l(l));
    return out;
  }

  static public Method findSingleInterfaceMethod(Class intrface) {
    return singletonOpt(findFunctionalInterfaceMethods(intrface));
  }

  static public Object convertPrimitiveIfNecessary(Object o, Class b) {
    if (b.isPrimitive()) {
      if (o instanceof Character) {
        char x = (char) (((Character) o));
        if (b == int.class)
          return Integer.valueOf(x);
        if (b == long.class)
          return Long.valueOf(x);
        if (b == float.class)
          return Float.valueOf(x);
        if (b == double.class)
          return Double.valueOf(x);
      } else if (o instanceof Number) {
        if (b == char.class)
          return Character.valueOf((char) ((Number) o).intValue());
        if (b == short.class)
          return ((Number) o).shortValue();
        if (b == int.class)
          return ((Number) o).intValue();
        if (b == long.class)
          return ((Number) o).longValue();
        if (b == float.class)
          return ((Number) o).floatValue();
      }
    }
    return o;
  }

  static public <A> IterableIterator<A> emptyItIt() {
    return emptyIterableIterator();
  }

  static public <A> IterableIterator<A> arrayIterator(A[] l) {
    return l == null ? null : new IterableIterator<A>() {

      public int i = 0;

      public boolean hasNext() {
        return i < l.length;
      }

      public A next() {
        return l[i++];
      }
    };
  }

  static public <A> IterableIterator<A> iff(Object f) {
    return iteratorFromFunction_withEndMarker(f);
  }

  static public IterableIterator iff(F0 f) {
    return iteratorFromFunction_withEndMarker(f);
  }

  static public <A> IterableIterator iff(IF0 f) {
    return iteratorFromFunction_withEndMarker(f);
  }

  static public Object endMarker() {
    return iteratorFromFunction_endMarker;
  }

  static public boolean structure_showTiming, structure_checkTokenCount;

  static public String structure(Object o) {
    return structure(o, new structure_Data());
  }

  static public String structure(Object o, structure_Data d) {
    StringWriter sw = new StringWriter();
    d.out = new PrintWriter(sw);
    structure_go(o, d);
    String s = str(sw);
    if (structure_checkTokenCount) {
      print("token count=" + d.n);
      assertEquals("token count", l(javaTokC(s)), d.n);
    }
    return s;
  }

  static public void structure_go(Object o, structure_Data d) {
    structure_1(o, d);
    while (nempty(d.stack)) popLast(d.stack).run();
  }

  static public void structureToPrintWriter(Object o, PrintWriter out) {
    structureToPrintWriter(o, out, new structure_Data());
  }

  static public void structureToPrintWriter(Object o, PrintWriter out, structure_Data d) {
    d.out = out;
    structure_go(o, d);
  }

  static public boolean structure_allowShortening = false;

  static public class structure_ClassInfo<A> {

    public Class c;

    public String shortName;

    public List<Field> fields;

    public Method customSerializer;

    public IVF1<Object> serializeObject;

    public boolean special = false;

    public boolean nullInstances = false;

    public boolean javafy = false;

    public Object emptyInstance;

    public String toString() {
      return commaCombine("Class " + className(c), stringIf("special", special), stringIf("customSerializer", customSerializer != null), stringIf("javafy", javafy), stringIf("nullInstances", nullInstances));
    }

    public void nullInstances(boolean b) {
      this.nullInstances = b;
      if (b)
        special = true;
    }

    public void javafy(boolean b) {
      this.javafy = b;
      if (b)
        special = true;
    }

    public boolean handle(A o) {
      return false;
    }
  }

  static public class structure_Data {

    public PrintWriter out;

    public int stringSizeLimit;

    public int shareStringsLongerThan = 20;

    public boolean noStringSharing = false;

    public boolean storeBaseClasses = false;

    public boolean honorFieldOrder = true;

    public String mcDollar = actualMCDollar();

    final public structure_Data setWarnIfUnpersistable(boolean warnIfUnpersistable) {
      return warnIfUnpersistable(warnIfUnpersistable);
    }

    public structure_Data warnIfUnpersistable(boolean warnIfUnpersistable) {
      this.warnIfUnpersistable = warnIfUnpersistable;
      return this;
    }

    final public boolean getWarnIfUnpersistable() {
      return warnIfUnpersistable();
    }

    public boolean warnIfUnpersistable() {
      return warnIfUnpersistable;
    }

    public boolean warnIfUnpersistable = true;

    final public structure_Data setStackTraceIfUnpersistable(boolean stackTraceIfUnpersistable) {
      return stackTraceIfUnpersistable(stackTraceIfUnpersistable);
    }

    public structure_Data stackTraceIfUnpersistable(boolean stackTraceIfUnpersistable) {
      this.stackTraceIfUnpersistable = stackTraceIfUnpersistable;
      return this;
    }

    final public boolean getStackTraceIfUnpersistable() {
      return stackTraceIfUnpersistable();
    }

    public boolean stackTraceIfUnpersistable() {
      return stackTraceIfUnpersistable;
    }

    public boolean stackTraceIfUnpersistable = true;

    final public structure_Data setSkipDefaultValues(boolean skipDefaultValues) {
      return skipDefaultValues(skipDefaultValues);
    }

    public structure_Data skipDefaultValues(boolean skipDefaultValues) {
      this.skipDefaultValues = skipDefaultValues;
      return this;
    }

    final public boolean getSkipDefaultValues() {
      return skipDefaultValues();
    }

    public boolean skipDefaultValues() {
      return skipDefaultValues;
    }

    public boolean skipDefaultValues = false;

    public structure_Data d() {
      return this;
    }

    transient public IF1<Field, Boolean> shouldIncludeField;

    public boolean shouldIncludeField(Field f) {
      return shouldIncludeField != null ? shouldIncludeField.get(f) : shouldIncludeField_base(f);
    }

    final public boolean shouldIncludeField_fallback(IF1<Field, Boolean> _f, Field f) {
      return _f != null ? _f.get(f) : shouldIncludeField_base(f);
    }

    public boolean shouldIncludeField_base(Field f) {
      return true;
    }

    public IdentityHashMap<Object, Integer> seen = new IdentityHashMap();

    public HashMap<String, Integer> strings = new HashMap();

    public HashSet<String> concepts = new HashSet();

    public HashMap<Class, structure_ClassInfo> infoByClass = new HashMap();

    public HashMap<Class, IF1<Object, Map>> persistenceInfo = new HashMap();

    public int n;

    public List<Runnable> stack = new ArrayList();

    public structure_Data append(String token) {
      out.print(token);
      ++n;
      return this;
    }

    public structure_Data append(int i) {
      out.print(i);
      ++n;
      return this;
    }

    public structure_Data append(String token, int tokCount) {
      out.print(token);
      n += tokCount;
      return this;
    }

    public structure_Data app(String token) {
      out.print(token);
      return this;
    }

    public structure_Data app(int i) {
      out.print(i);
      return this;
    }

    public structure_Data app(char c) {
      out.print(c);
      return this;
    }

    public structure_ClassInfo infoForClass(Class c) {
      structure_ClassInfo info = infoByClass.get(c);
      if (info == null)
        info = newClass(c);
      return info;
    }

    transient public IF1<String, String> realShortName;

    public String realShortName(String name) {
      return realShortName != null ? realShortName.get(name) : realShortName_base(name);
    }

    final public String realShortName_fallback(IF1<String, String> _f, String name) {
      return _f != null ? _f.get(name) : realShortName_base(name);
    }

    public String realShortName_base(String name) {
      return dropPrefix("main$", dropPrefix("loadableUtils.utils$", dropPrefix(mcDollar, name)));
    }

    public structure_ClassInfo newClass(Class c) {
      var d = d();
      boolean isJavaXClass = isJavaXClassName(c.getName(), mcDollar);
      if (c == String.class)
        return new structure_ClassInfo<String>() {

          @Override
          public boolean handle(String o) {
            String s = d.stringSizeLimit != 0 ? shorten((String) o, d.stringSizeLimit) : (String) o;
            if (!d.noStringSharing) {
              if (d.shareStringsLongerThan == Integer.MAX_VALUE)
                d.seen.put(o, d.n);
              if (l(s) >= d.shareStringsLongerThan)
                d.strings.put(s, d.n);
            }
            quoteToPrintWriter(s, d.out);
            d.n++;
            return true;
          }
        };
      if (c == File.class)
        return new structure_ClassInfo<File>() {

          @Override
          public boolean handle(File o) {
            append("File ").append(quote(o.getPath()));
            return true;
          }
        };
      if (!isJavaXClass) {
        if (isSubClassOf(c, Set.class))
          return new structure_ClassInfo<Set>() {

            @Override
            public boolean handle(Set o) {
              writeSet(o);
              return true;
            }
          };
        if (isSubClassOf(c, Collection.class))
          return new structure_ClassInfo<Collection>() {

            @Override
            public boolean handle(Collection o) {
              writeCollection(o);
              return true;
            }
          };
        if (isSubClassOf(c, Map.class))
          return new structure_ClassInfo<Map>() {

            @Override
            public boolean handle(Map o) {
              writeMap(o);
              return true;
            }
          };
      }
      structure_ClassInfo info = new structure_ClassInfo();
      info.c = c;
      infoByClass.put(c, info);
      String name = c.getName();
      String shortName = realShortName(name);
      if (startsWithDigit(shortName))
        shortName = name;
      info.shortName = shortName;
      try {
        if (isSyntheticOrAnonymous(c)) {
          info.nullInstances(true);
          return info;
        }
        if (c.isEnum()) {
          info.special = true;
          return info;
        }
        if (isSubClassOf(c, SynchronizedMap.class))
          return new structure_ClassInfo<SynchronizedMap>() {

            @Override
            public boolean handle(SynchronizedMap o) {
              append("sync ");
              structure_1(o.m, d);
              return true;
            }
          };
        if (isSubClassOf(c, SynchronizedList.class))
          return new structure_ClassInfo<SynchronizedList>() {

            @Override
            public boolean handle(SynchronizedList o) {
              append("sync ");
              structure_1(unwrapSynchronizedList(o), d);
              return true;
            }
          };
        if (c.isArray()) {
          return info;
        }
        if ((info.customSerializer = findMethodNamed(c, "_serialize")) != null)
          info.special = true;
        if (storeBaseClasses) {
          Class sup = c.getSuperclass();
          if (sup != Object.class) {
            append("bc ");
            append(shortDynClassNameForStructure(c));
            out.print(" ");
            append(shortDynClassNameForStructure(sup));
            out.print(" ");
            infoForClass(sup);
          }
        }
        if (eqOneOf(name, "java.awt.Color", "java.lang.ThreadLocal"))
          info.javafy(true);
        else if (name.startsWith("sun") || !isPersistableClass(c)) {
          info.javafy(true);
          if (warnIfUnpersistable) {
            String msg = "Class not persistable: " + c + " (anonymous or no default constructor), referenced from " + last(stack);
            if (stackTraceIfUnpersistable)
              printStackTrace(new Throwable(msg));
            else
              print(msg);
          }
        } else if (skipDefaultValues) {
          var ctor = getDefaultConstructor(c);
          if (ctor != null)
            info.emptyInstance = invokeConstructor(ctor);
        }
      } catch (Throwable e) {
        printStackTrace(e);
        info.nullInstances(true);
      }
      return info;
    }

    public void setFields(structure_ClassInfo info, List<Field> fields) {
      info.fields = fields;
    }

    public void writeObject(Object o, String shortName, Map<String, Object> fv) {
      String singleField = fv.size() == 1 ? first(fv.keySet()) : null;
      append(shortName);
      n += countDots(shortName) * 2;
      int l = n;
      Iterator it = fv.entrySet().iterator();
      class WritingObject implements Runnable {

        public String lastFieldWritten;

        public void run() {
          try {
            if (!it.hasNext()) {
              if (n != l)
                append(")");
            } else {
              Map.Entry e = (Map.Entry) (it.next());
              append(n == l ? "(" : ", ");
              append(lastFieldWritten = (String) e.getKey()).append("=");
              stack.add(this);
              structure_1(e.getValue(), structure_Data.this);
            }
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return shortName + "." + lastFieldWritten;
        }
      }
      stack.add(new WritingObject());
    }

    public void writeMap(Map o) {
      var d = this;
      String name = o.getClass().getName();
      if (o instanceof LinkedHashMap)
        d.append("lhm");
      else if (o instanceof HashMap)
        d.append("hm");
      else if (o instanceof TreeMap)
        d.append(isCIMap_gen((TreeMap) o) ? "cimap" : "tm");
      else if (name.equals("java.util.Collections$SynchronizedMap") || name.equals("java.util.Collections$SynchronizedSortedMap") || name.equals("java.util.Collections$SynchronizedNavigableMap")) {
        d.append("sync ");
        {
          structure_1(unwrapSynchronizedMap(((Map) o)), d);
          return;
        }
      }
      d.append("{");
      final int l = d.n;
      final Iterator it = cloneMap((Map) o).entrySet().iterator();
      class WritingMap implements Runnable {

        public boolean v = false;

        public Map.Entry e;

        public String toString() {
          return renderVars("WritingMap", "e", mapEntryToPair(e), "v", !v);
        }

        public void run() {
          try {
            if (v) {
              d.append("=");
              v = false;
              d.stack.add(this);
              structure_1(e.getValue(), d);
            } else {
              if (!it.hasNext())
                d.append("}");
              else {
                e = (Map.Entry) it.next();
                v = true;
                d.stack.add(this);
                if (d.n != l)
                  d.append(", ");
                structure_1(e.getKey(), d);
              }
            }
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }
      }
      d.stack.add(new WritingMap());
    }

    public void writeSet(Set o) {
      var d = this;
      String name = o.getClass().getName();
      if (o instanceof TreeSet) {
        d.append(isCISet_gen(o) ? "ciset" : "treeset");
        structure_1(new ArrayList(o), d);
        return;
      }
      d.append(o instanceof LinkedHashSet ? "lhs" : "hashset");
      structure_1(new ArrayList(o), d);
    }

    public void writeCollection(Collection o) {
      var d = this;
      String name = o.getClass().getName();
      if (name.equals("java.util.Collections$SynchronizedList") || name.equals("java.util.Collections$SynchronizedRandomAccessList")) {
        d.append("sync ");
        {
          structure_1(unwrapSynchronizedList(((List) o)), d);
          return;
        }
      } else if (name.equals("java.util.LinkedList"))
        d.append("ll");
      d.append("[");
      int l = d.n;
      Iterator it = cloneList(o).iterator();
      d.stack.add(new Runnable() {

        public void run() {
          try {
            if (!it.hasNext())
              d.append("]");
            else {
              d.stack.add(this);
              if (d.n != l)
                d.append(", ");
              structure_1(it.next(), d);
            }
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "if (!it.hasNext())\r\n        d.append(\"]\");\r\n      else {\r\n        d.stack.add...";
        }
      });
    }
  }

  static public void structure_1(Object o, structure_Data d) {
    try {
      if (o == null) {
        d.append("null");
        return;
      }
      Class c = o.getClass();
      boolean concept = false;
      concept = o instanceof Concept;
      structure_ClassInfo info = d.infoForClass(c);
      boolean isJavaXName = isJavaXClassName(c.getName(), d.mcDollar);
      boolean referencable = isJavaXName && !(o instanceof Number || o instanceof Character || o instanceof Boolean) || o instanceof Collection || o instanceof Map || o instanceof Rectangle;
      if (referencable) {
        Integer ref = d.seen.get(o);
        if (ref != null) {
          d.append("t").app(ref);
          return;
        }
        d.seen.put(o, d.n);
      }
      if (info.handle(o)) {
        return;
      }
      if (info.special) {
        if (info.javafy) {
          d.append("j ").append(quote(str(o)));
          return;
        }
        if (c.isEnum()) {
          d.append("enum ");
          d.append(info.shortName);
          d.out.append(' ');
          d.append(((Enum) o).ordinal());
          return;
        }
        if (info.customSerializer != null) {
          Object o2 = invokeMethod(info.customSerializer, o);
          if (o2 == o) {
          } else {
            d.append("cu ");
            String name = c.getName();
            String shortName = d.realShortName(name);
            d.append(shortName);
            d.out.append(' ');
            structure_1(o2, d);
            return;
          }
        } else if (info.nullInstances) {
          d.append("null");
          return;
        } else if (info.serializeObject != null) {
          info.serializeObject.get(o);
          return;
        } else
          throw fail("unknown special type");
      }
      List<Field> lFields = info.fields;
      if (lFields == null) {
        if (o instanceof Number) {
          PrintWriter out = d.out;
          if (o instanceof Integer) {
            int i = ((Integer) o).intValue();
            out.print(i);
            d.n += i < 0 ? 2 : 1;
            return;
          }
          if (o instanceof Long) {
            long l = ((Long) o).longValue();
            out.print(l);
            out.print("L");
            d.n += l < 0 ? 2 : 1;
            return;
          }
          if (o instanceof Short) {
            short s = ((Short) o).shortValue();
            d.append("sh ");
            out.print(s);
            d.n += s < 0 ? 2 : 1;
            return;
          }
          if (o instanceof Float) {
            d.append("fl ", 2);
            quoteToPrintWriter(str(o), out);
            return;
          }
          if (o instanceof Double) {
            d.append("d(", 3);
            quoteToPrintWriter(str(o), out);
            d.append(")");
            return;
          }
          if (o instanceof BigInteger) {
            out.print("bigint(");
            out.print(o);
            out.print(")");
            d.n += ((BigInteger) o).signum() < 0 ? 5 : 4;
            return;
          }
        }
        if (o instanceof Boolean) {
          d.append(((Boolean) o).booleanValue() ? "t" : "f");
          return;
        }
        if (o instanceof Character) {
          d.append(quoteCharacter((Character) o));
          return;
        }
        String name = c.getName();
        if (!isJavaXName) {
          if (o instanceof Set) {
            d.writeSet((Set) o);
            return;
          }
          if (o instanceof Collection) {
            {
              d.writeCollection((Collection) o);
              return;
            }
          }
          if (o instanceof Map) {
            d.writeMap((Map) o);
            return;
          }
        }
        if (c.isArray()) {
          if (o instanceof byte[]) {
            d.append("ba ").append(quote(bytesToHex((byte[]) o)));
            return;
          }
          final int n = Array.getLength(o);
          if (o instanceof boolean[]) {
            String hex = boolArrayToHex((boolean[]) o);
            int i = l(hex);
            while (i > 0 && hex.charAt(i - 1) == '0' && hex.charAt(i - 2) == '0') i -= 2;
            d.append("boolarray ").append(n).app(" ").append(quote(substring(hex, 0, i)));
            return;
          }
          if (o instanceof short[]) {
            String hex = shortArrayToHex_bigEndian((short[]) o);
            d.append("shortarray \"").append(hex).app('\"');
            return;
          }
          if (o instanceof long[]) {
            String hex = longArrayToHex_bigEndian((long[]) o);
            d.append("longarray \"").append(hex).app('\"');
            return;
          }
          String atype = "array";
          if (o instanceof int[]) {
            atype = "intarray";
          } else if (o instanceof double[]) {
            atype = "dblarray";
          } else if (o instanceof float[]) {
            atype = "floatarray";
          } else {
            Pair<Class, Integer> p = arrayTypeAndDimensions(c);
            if (p.a == int.class)
              atype = "intarray";
            else if (p.a == byte.class)
              atype = "bytearray";
            else if (p.a == boolean.class)
              atype = "boolarray";
            else if (p.a == double.class)
              atype = "dblarray";
            else if (p.a == float.class)
              atype = "floatarray";
            else if (p.a == String.class) {
              atype = "array S";
              d.n++;
            } else
              atype = "array";
            if (p.b > 1) {
              atype += "/" + p.b;
              d.n += 2;
            }
          }
          d.append(atype).append("{");
          d.stack.add(new Runnable() {

            public int i;

            public void run() {
              if (i >= n)
                d.append("}");
              else {
                d.stack.add(this);
                if (i > 0)
                  d.append(", ");
                structure_1(Array.get(o, i++), d);
              }
            }
          });
          return;
        }
        if (o instanceof Class) {
          d.append("class(", 2).append(quote(((Class) o).getName())).append(")");
          return;
        }
        if (o instanceof Throwable) {
          d.append("exception(", 2).append(quote(((Throwable) o).getMessage())).append(")");
          return;
        }
        if (o instanceof BitSet) {
          BitSet bs = (BitSet) o;
          d.append("bitset{", 2);
          int l = d.n;
          for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
            if (d.n != l)
              d.append(", ");
            d.append(i);
          }
          d.append("}");
          return;
        }
        String dynName = shortDynClassNameForStructure(o);
        if (concept && !d.concepts.contains(dynName)) {
          d.concepts.add(dynName);
          d.append("c ");
        }
        TreeSet<Field> fields = new TreeSet<Field>(new Comparator<Field>() {

          public int compare(Field a, Field b) {
            return stdcompare(a.getName(), b.getName());
          }
        });
        Class cc = c;
        while (cc != Object.class) {
          for (Field field : getDeclaredFields_cached(cc)) {
            if (!d.shouldIncludeField(field))
              continue;
            String fieldName = field.getName();
            if (fieldName.equals("_persistenceInfo"))
              d.persistenceInfo.put(c, obj -> (Map) fieldGet(field, obj));
            if ((field.getModifiers() & (java.lang.reflect.Modifier.STATIC | java.lang.reflect.Modifier.TRANSIENT)) != 0)
              continue;
            fields.add(field);
          }
          cc = cc.getSuperclass();
        }
        Method persistenceInfoMethod = findInstanceMethod(c, "_persistenceInfo");
        if (persistenceInfoMethod != null)
          d.persistenceInfo.put(c, obj -> (Map) invokeMethod(persistenceInfoMethod, obj));
        lFields = asList(d.honorFieldOrder ? fieldObjectsInFieldOrder(c, fields) : fields);
        int n = l(lFields);
        for (int i = 0; i < n; i++) {
          Field f = lFields.get(i);
          if (f.getName().startsWith("this$")) {
            lFields.remove(i);
            lFields.add(0, f);
            break;
          }
        }
        d.setFields(info, lFields);
      }
      IF1<Object, Map> piGetter = d.persistenceInfo.get(c);
      Map persistenceInfo = piGetter == null ? null : piGetter.get(o);
      if (piGetter == null && o instanceof DynamicObject)
        persistenceInfo = (Map) getOptDynOnly(((DynamicObject) o), "_persistenceInfo");
      LinkedHashMap<String, Object> fv = new LinkedHashMap();
      Object defaultInstance = info.emptyInstance;
      if (!referencable)
        throw fail("Not referencable: " + className(o));
      for (Field f : lFields) {
        Object value, defaultValue = null;
        try {
          value = f.get(o);
          defaultValue = defaultInstance == null ? null : f.get(defaultInstance);
        } catch (Exception e) {
          value = "?";
        }
        if (!eq(defaultValue, value) && (persistenceInfo == null || !Boolean.FALSE.equals(persistenceInfo.get(f.getName()))))
          fv.put(f.getName(), value);
      }
      String shortName = info.shortName;
      Object classNameFromFV = fv.get("className");
      if (concept && eq(classNameFromFV, shortName))
        fv.remove("className");
      if (o instanceof DynamicObject) {
        putAll(fv, (Map) fv.get("fieldValues"));
        fv.remove("fieldValues");
        if (((DynamicObject) o).className != null) {
          shortName = shortDynClassNameForStructure((DynamicObject) o);
          fv.remove("className");
          if (eq(shortName, "java.lang.Class")) {
            d.append("class(");
            d.append(quoted(fv.get("name")));
            d.append(")");
            return;
          }
        }
      }
      d.writeObject(o, shortName, fv);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public List<String> javaTok_noMLS(String s) {
    ArrayList<String> tok = new ArrayList();
    int l = s == null ? 0 : s.length();
    int i = 0, n = 0;
    while (i < l) {
      int j = i;
      char c, d;
      while (j < l) {
        c = s.charAt(j);
        d = j + 1 >= l ? '\0' : s.charAt(j + 1);
        if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
          ++j;
        else if (c == '/' && d == '*') {
          do ++j; while (j < l && !s.substring(j, Math.min(j + 2, l)).equals("*/"));
          j = Math.min(j + 2, l);
        } else if (c == '/' && d == '/') {
          do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0);
        } else
          break;
      }
      tok.add(javaTok_substringN(s, i, j));
      ++n;
      i = j;
      if (i >= l)
        break;
      c = s.charAt(i);
      d = i + 1 >= l ? '\0' : s.charAt(i + 1);
      if (c == '\'' || c == '"') {
        char opener = c;
        ++j;
        while (j < l) {
          int c2 = s.charAt(j);
          if (c2 == opener || c2 == '\n' && opener == '\'') {
            ++j;
            break;
          } else if (c2 == '\\' && j + 1 < l)
            j += 2;
          else
            ++j;
        }
      } else if (Character.isJavaIdentifierStart(c))
        do ++j; while (j < l && Character.isJavaIdentifierPart(s.charAt(j)));
      else if (Character.isDigit(c)) {
        do ++j; while (j < l && Character.isDigit(s.charAt(j)));
        if (j < l && s.charAt(j) == 'L')
          ++j;
      } else
        ++j;
      tok.add(javaTok_substringC(s, i, j));
      ++n;
      i = j;
    }
    if ((tok.size() % 2) == 0)
      tok.add("");
    return tok;
  }

  static public Object swingCall(final Object o, final String method, final Object... args) {
    return swing(new F0<Object>() {

      public Object get() {
        try {
          return call(o, method, args);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "return call(o, method, args);";
      }
    });
  }

  static public ListIterator emptyListIterator() {
    return Collections.emptyListIterator();
  }

  static public AutoCloseable tempSetField(Object o, String field, Object value) {
    final Object oldValue = get(o, field);
    set(o, field, value);
    return () -> set(o, field, oldValue);
  }

  static public <A> AutoCloseable tempSetField(ISetAndGet<A> gs, A value) {
    if (gs == null)
      return null;
    A oldValue = gs.get();
    gs.set(value);
    return () -> gs.set(oldValue);
  }

  static public <A> AutoCloseable tempSetField(A value, ISetAndGet<A> gs) {
    return tempSetField(gs, value);
  }

  static public int identityHash(Object o) {
    return identityHashCode(o);
  }

  static public String firstToLower(String s) {
    if (empty(s))
      return s;
    return Character.toLowerCase(s.charAt(0)) + s.substring(1);
  }

  static public IMeta toIMeta(Object o) {
    return initIMeta(o);
  }

  static public IMeta initIMeta(Object o) {
    if (o == null)
      return null;
    if (o instanceof IMeta)
      return ((IMeta) o);
    if (o instanceof JComponent)
      return initMetaOfJComponent((JComponent) o);
    if (o instanceof BufferedImage)
      return optCast(IMeta.class, ((BufferedImage) o).getProperty("meta"));
    return null;
  }

  static public Map makeObjectMetaMap() {
    return new CompactHashMap();
  }

  static public IAutoCloseableF0 tempMetaMutex(IMeta o) {
    return o == null ? null : o._tempMetaMutex();
  }

  static public <A, B> void syncMapPut2(Map<A, B> map, A key, B value) {
    if (map != null && key != null)
      synchronized (collectionMutex(map)) {
        if (value != null)
          map.put(key, value);
        else
          map.remove(key);
      }
  }

  static public ClassLoader getClassLoader(Object o) {
    return o == null ? null : _getClass(o).getClassLoader();
  }

  static public boolean dirOrZipContainsPath(File location, String subPath) {
    try {
      if (location.isDirectory()) {
        return new File(location, subPath).exists();
      } else if (location.isFile()) {
        return zipFileContains_falseOnError(location, subPath);
      }
      return false;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public File[] listFiles(File dir) {
    File[] files = dir == null ? null : dir.listFiles();
    return files == null ? new File[0] : files;
  }

  static public File[] listFiles(String dir) {
    return listFiles(new File(dir));
  }

  static public String dropPrefixOrNull(String prefix, String s) {
    return s != null && s.startsWith(prefix) ? s.substring(l(prefix)) : null;
  }

  static public List concatMap(Object f, Iterable l) {
    return concatLists(map(f, l));
  }

  static public List concatMap(Iterable l, Object f) {
    return concatMap(f, l);
  }

  static public List concatMap(Object f, Object[] l) {
    return concatLists(map(f, l));
  }

  static public List concatMap(Object[] l, Object f) {
    return concatMap(f, l);
  }

  static public <A, B, C extends Iterable<B>> List<B> concatMap(Iterable<A> l, IF1<A, C> f) {
    return concatMap(l, (Object) f);
  }

  static public <A, B, C extends Iterable<B>> List<B> concatMap(IF1<A, C> f, Iterable<A> l) {
    return concatMap(l, f);
  }

  static public <A, B, C extends Iterable<B>> List<B> concatMap(IF1<A, C> f, A[] l) {
    return concatMap((Object) f, l);
  }

  static public List<String> loadedJigsawModuleNames() {
    var moduleLayer = ModuleLayer.boot();
    var modules = moduleLayer.modules();
    return map(modules, mod -> mod.getName());
  }

  static public List<String> classNamesInJigsawModule(String moduleName) {
    return classNamesInJigsawModule(moduleName, javaHome());
  }

  static public List<String> classNamesInJigsawModule(String moduleName, File javaHome) {
    return classNamesInJarOrDir(jigsawModuleFile(moduleName, javaHome), "classes/");
  }

  static public Runnable _topLevelErrorHandling(Runnable r) {
    if (r == null)
      return null;
    Object info = _threadInfo();
    Object mod = dm_current_generic();
    Runnable r2 = r;
    if (info != null || mod == null)
      r2 = new Runnable() {

        public void run() {
          try {
            AutoCloseable __1 = (AutoCloseable) (rcall("enter", mod));
            try {
              _threadInheritInfo(info);
              r.run();
            } finally {
              _close(__1);
            }
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "temp (AutoCloseable) rcall enter(mod);\r\n      _threadInheritInfo(info);\r\n    ...";
        }
      };
    r2 = rPcall(r2);
    return r2;
  }

  static public String defaultThreadName_name;

  static public String defaultThreadName() {
    if (defaultThreadName_name == null)
      defaultThreadName_name = "A thread by " + programID();
    return defaultThreadName_name;
  }

  static public <A> List<A> cloneSubList(List<A> l, int startIndex, int endIndex) {
    return newSubList(l, startIndex, endIndex);
  }

  static public <A> List<A> cloneSubList(List<A> l, int startIndex) {
    return newSubList(l, startIndex);
  }

  static public void removeSubList(List l, int from, int to) {
    if (l != null)
      subList(l, from, to).clear();
  }

  static public void removeSubList(List l, int from) {
    if (l != null)
      subList(l, from).clear();
  }

  static public List<String> getJavaModifiers_list = litlist("static", "abstract", "public", "private", "protected", "final", "native", "volatile", "synchronized", "transient", "default");

  static public List<String> getJavaModifiers() {
    return getJavaModifiers_list;
  }

  public static String bytesToHex(byte[] bytes) {
    return bytesToHex(bytes, 0, bytes.length);
  }

  public static String bytesToHex(byte[] bytes, int ofs, int len) {
    StringBuilder stringBuilder = new StringBuilder(len * 2);
    for (int i = 0; i < len; i++) {
      String s = "0" + Integer.toHexString(bytes[ofs + i]);
      stringBuilder.append(s.substring(s.length() - 2, s.length()));
    }
    return stringBuilder.toString();
  }

  static public byte[] md5AsByteArray(byte[] data) {
    try {
      return MessageDigest.getInstance("MD5").digest(data);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public byte[] toUtf8(String s) {
    try {
      return s.getBytes(utf8charset());
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public boolean md5OfFile_verbose = false;

  static public String md5OfFile(String path) {
    return md5OfFile(newFile(path));
  }

  static public String md5OfFile(File f) {
    try {
      if (!f.exists())
        return "-";
      if (md5OfFile_verbose)
        print("Getting MD5 of " + f);
      MessageDigest md5 = MessageDigest.getInstance("MD5");
      FileInputStream in = new FileInputStream(f);
      try {
        byte[] buf = new byte[65536];
        int l;
        while (true) {
          l = in.read(buf);
          if (l <= 0)
            break;
          md5.update(buf, 0, l);
        }
        return bytesToHex(md5.digest());
      } finally {
        _close(in);
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Class getRawTypeClass(Type t) {
    return typeToClass(t);
  }

  static public String classNameToByteCodeFormat(String className) {
    return "L" + className.replace('.', '/');
  }

  static public String classNameToByteCodeFormat(Class c) {
    return classNameToByteCodeFormat(className(c));
  }

  static public String joinMap(Object f, Iterable l) {
    return join(map(f, l));
  }

  static public String joinMap(Iterable l, Object f) {
    return joinMap(f, l);
  }

  static public <A> String joinMap(Iterable<A> l, IF1<A, String> f) {
    return joinMap(f, l);
  }

  static public <A> String joinMap(A[] l, IF1<A, String> f) {
    return joinMap(f, l);
  }

  static public <A> String joinMap(IF1<A, String> f, Iterable<A> l) {
    return join(map(f, l));
  }

  static public <A> String joinMap(IF1<A, String> f, A[] l) {
    return join(map(f, l));
  }

  static public <A, B> String joinMap(String separator, Map<A, B> map, IF2<A, B, String> f) {
    return join(separator, map(map, f));
  }

  static public boolean isArrayType(Class type) {
    return type != null && type.isArray();
  }

  static public Object[] mapToObjectArray(Map map) {
    List l = new ArrayList();
    for (Object o : keys(map)) {
      l.add(o);
      l.add(map.get(o));
    }
    return toObjectArray(l);
  }

  static public Object[] mapToObjectArray(Object f, Collection l) {
    int n = l(l);
    Object[] array = new Object[n];
    if (n != 0) {
      Iterator it = iterator(l);
      for (int i = 0; i < n; i++) array[i] = callF(f, it.next());
    }
    return array;
  }

  static public Object[] mapToObjectArray(Object f, Object[] l) {
    int n = l(l);
    Object[] array = new Object[n];
    for (int i = 0; i < n; i++) array[i] = callF(f, l[i]);
    return array;
  }

  static public <A> Object[] mapToObjectArray(Collection<A> l, IF1<A, Object> f) {
    return mapToObjectArray(f, l);
  }

  static public <A> Object[] mapToObjectArray(A[] l, IF1<A, Object> f) {
    return mapToObjectArray(f, l);
  }

  static public <A> Object[] mapToObjectArray(IF1<A, Object> f, A[] l) {
    int n = l(l);
    Object[] array = new Object[n];
    for (int i = 0; i < n; i++) array[i] = f.get(l[i]);
    return array;
  }

  static public <A> Object[] mapToObjectArray(IF1<A, Object> f, Collection<A> l) {
    int n = l(l);
    Object[] array = new Object[n];
    if (n != 0) {
      Iterator it = iterator(l);
      for (int i = 0; i < n; i++) array[i] = callF(f, it.next());
    }
    return array;
  }

  static public Object[] arrayrep(Object a, int n) {
    Object[] array = new Object[n];
    for (int i = 0; i < n; i++) array[i] = a;
    return array;
  }

  static public <A> A[] arrayrep(Class<A> type, A a, int n) {
    A[] array = newArray(type, n);
    for (int i = 0; i < n; i++) array[i] = a;
    return array;
  }

  static public void setOpt_raw(Object o, String field, Object value) {
    try {
      if (o == null)
        return;
      if (o instanceof Class)
        setOpt_raw((Class) o, field, value);
      else {
        Field f = setOpt_raw_findField(o.getClass(), field);
        if (f != null) {
          makeAccessible(f);
          smartSet(f, o, value);
        }
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public void setOpt_raw(Class c, String field, Object value) {
    try {
      if (c == null)
        return;
      Field f = setOpt_raw_findStaticField(c, field);
      if (f != null) {
        makeAccessible(f);
        smartSet(f, null, value);
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Field setOpt_raw_findStaticField(Class<?> c, String field) {
    Class _c = c;
    do {
      for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0)
        return f;
      _c = _c.getSuperclass();
    } while (_c != null);
    return null;
  }

  static public Field setOpt_raw_findField(Class<?> c, String field) {
    Class _c = c;
    do {
      for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field))
        return f;
      _c = _c.getSuperclass();
    } while (_c != null);
    return null;
  }

  static public <A extends DynamicObject> A setDyn(A o, String key, Object value) {
    setDynObjectValue(o, key, value);
    return o;
  }

  static public void setDyn(IMeta o, String key, Object value) {
    metaMapPut(o, key, value);
  }

  static public boolean regionMatches(String a, int offsetA, String b, int offsetB, int len) {
    return a != null && b != null && a.regionMatches(offsetA, b, offsetB, len);
  }

  static public boolean regionMatches(String a, int offsetA, String b) {
    return regionMatches(a, offsetA, b, 0, l(b));
  }

  static public String javaTok_substringN(String s, int i, int j) {
    if (i == j)
      return "";
    if (j == i + 1 && s.charAt(i) == ' ')
      return " ";
    return s.substring(i, j);
  }

  static public String javaTok_substringC(String s, int i, int j) {
    return s.substring(i, j);
  }

  static public List<String> javaTokWithExisting(String s, List<String> existing) {
    ++javaTok_n;
    int nExisting = javaTok_opt && existing != null ? existing.size() : 0;
    ArrayList<String> tok = existing != null ? new ArrayList(nExisting) : new ArrayList();
    int l = s.length();
    int i = 0, n = 0;
    while (i < l) {
      int j = i;
      char c, d;
      while (j < l) {
        c = s.charAt(j);
        d = j + 1 >= l ? '\0' : s.charAt(j + 1);
        if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
          ++j;
        else if (c == '/' && d == '*') {
          do ++j; while (j < l && !s.substring(j, Math.min(j + 2, l)).equals("*/"));
          j = Math.min(j + 2, l);
        } else if (c == '/' && d == '/') {
          do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0);
        } else
          break;
      }
      if (n < nExisting && javaTokWithExisting_isCopyable(existing.get(n), s, i, j))
        tok.add(existing.get(n));
      else
        tok.add(javaTok_substringN(s, i, j));
      ++n;
      i = j;
      if (i >= l)
        break;
      c = s.charAt(i);
      d = i + 1 >= l ? '\0' : s.charAt(i + 1);
      if (c == '\'' && Character.isJavaIdentifierStart(d) && i + 2 < l && "'\\".indexOf(s.charAt(i + 2)) < 0) {
        j += 2;
        while (j < l && Character.isJavaIdentifierPart(s.charAt(j))) ++j;
      } else if (c == '\'' || c == '"') {
        char opener = c;
        ++j;
        while (j < l) {
          if (s.charAt(j) == opener) {
            ++j;
            break;
          } else if (s.charAt(j) == '\\' && j + 1 < l)
            j += 2;
          else
            ++j;
        }
      } else if (Character.isJavaIdentifierStart(c))
        do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || "'".indexOf(s.charAt(j)) >= 0));
      else if (Character.isDigit(c)) {
        do ++j; while (j < l && Character.isDigit(s.charAt(j)));
        if (j < l && s.charAt(j) == 'L')
          ++j;
      } else if (c == '[' && d == '[') {
        do ++j; while (j + 1 < l && !s.substring(j, j + 2).equals("]]"));
        j = Math.min(j + 2, l);
      } else if (c == '[' && d == '=' && i + 2 < l && s.charAt(i + 2) == '[') {
        do ++j; while (j + 2 < l && !s.substring(j, j + 3).equals("]=]"));
        j = Math.min(j + 3, l);
      } else
        ++j;
      if (n < nExisting && javaTokWithExisting_isCopyable(existing.get(n), s, i, j))
        tok.add(existing.get(n));
      else
        tok.add(javaTok_substringC(s, i, j));
      ++n;
      i = j;
    }
    if ((tok.size() % 2) == 0)
      tok.add("");
    javaTok_elements += tok.size();
    return tok;
  }

  static public boolean javaTokWithExisting_isCopyable(String t, String s, int i, int j) {
    return t.length() == j - i && s.regionMatches(i, t, 0, j - i);
  }

  static public boolean containsNewLine(String s) {
    return contains(s, '\n');
  }

  static public <A> boolean addToCollection(Collection<A> c, A a) {
    return c != null && c.add(a);
  }

  static public void printNumberedLines(Map map) {
    printNumberedLines(mapToLines(map));
  }

  static public void printNumberedLines(String prefix, Map map) {
    printNumberedLines(prefix, mapToLines(map));
  }

  static public <A extends Iterable> A printNumberedLines(A l) {
    int i = 0;
    if (l != null)
      for (Object a : cloneList(l)) print((++i) + ". " + str(a));
    return l;
  }

  static public <A extends Iterable> A printNumberedLines(String prefix, A l) {
    int i = 0;
    if (l != null)
      for (Object a : cloneList(l)) print(prefix + (++i) + ". " + str(a));
    return l;
  }

  static public void printNumberedLines(Object[] l) {
    printNumberedLines("", l);
  }

  static public void printNumberedLines(String prefix, Object[] l) {
    printNumberedLines(prefix, wrapAsList(l));
  }

  static public void printNumberedLines(Object o) {
    printNumberedLines(lines(str(o)));
  }

  static public String lines(Iterable lines) {
    return fromLines(lines);
  }

  static public String lines(Object[] lines) {
    return fromLines(asList(lines));
  }

  static public List<String> lines(String s) {
    return toLines(s);
  }

  static public <A> String lines(Iterable<A> l, IF1<A, String> f) {
    return mapToLines(l, f);
  }

  static public List<String> lines(File f) {
    return linesFromFile_list(f);
  }

  static public TimerTask timerTask(final Object r, final java.util.Timer timer) {
    return new TimerTask() {

      public void run() {
        if (!licensed())
          timer.cancel();
        else
          pcallF(r);
      }
    };
  }

  static public <A> A vmBus_timerStarted(A timer) {
    vmBus_send("timerStarted", timer, costCenter());
    return timer;
  }

  static public int methodApplicabilityScore_onTypes(Method m, Class[] argTypes) {
    return methodApplicabilityScore_onTypes((Executable) m, argTypes);
  }

  static public int methodApplicabilityScore_onTypes(Executable m, Class[] argTypes) {
    Class<?>[] types = m.getParameterTypes();
    if (types.length != argTypes.length)
      return Integer.MAX_VALUE;
    int score = 0;
    for (int i = 0; i < types.length; i++) {
      Class a = argTypes[i];
      Class c = types[i];
      if (c == a) {
      } else if (isSubclassOf(a, c))
        ++score;
      else
        return Integer.MAX_VALUE;
    }
    return score;
  }

  static public ThreadLocal<Boolean> assertVerbose_value = new ThreadLocal();

  static public void assertVerbose(boolean b) {
    assertVerbose_value.set(b);
  }

  static public boolean assertVerbose() {
    return isTrue(assertVerbose_value.get());
  }

  static public <A> A assertEqualsVerbose(Object x, A y) {
    assertEqualsVerbose((String) null, x, y);
    return y;
  }

  static public <A> A assertEqualsVerbose(String msg, Object x, A y) {
    if (!eq(x, y)) {
      throw fail((nempty(msg) ? msg + ": " : "") + "expected: " + x + ", got: " + y);
    } else
      print("OK" + (empty(msg) ? "" : " " + msg) + ": " + (x));
    return y;
  }

  static public void assertEqualsVerbose(Scorer scorer, Object x, Object y) {
    assertEqualsVerbose(scorer, "", x, y);
  }

  static public void assertEqualsVerbose(Scorer scorer, String msg, Object x, Object y) {
    if (scorer == null) {
      assertEqualsVerbose(x, y);
      return;
    }
    if (!eq(x, y)) {
      print(appendColonIfNempty(msg) + y + " != " + x);
      scorer.add(false);
    } else {
      print("OK: " + appendColonIfNempty(msg) + x);
      scorer.add(true);
    }
  }

  static public String nullIfEmpty(String s) {
    return isEmpty(s) ? null : s;
  }

  static public <A, B> Map<A, B> nullIfEmpty(Map<A, B> map) {
    return isEmpty(map) ? null : map;
  }

  static public <A> List<A> nullIfEmpty(List<A> l) {
    return isEmpty(l) ? null : l;
  }

  static public List<Method> findNonDefaultInterfaceMethods(Class intrface) {
    if (!isInterface(intrface))
      return null;
    List<Method> l = new ArrayList();
    _MethodCache cache = getMethodCache(intrface);
    Class c = intrface;
    do {
      for (Method m : c.getDeclaredMethods()) if (m.getDeclaringClass() != Object.class && !m.isDefault() && !isStaticMethod(m))
        l.add(m);
      c = c.getSuperclass();
    } while (c != null);
    return l;
  }

  static public int inc(AtomicInteger i) {
    return incAtomicInt(i);
  }

  static public int inc(AtomicInteger i, int delta) {
    return incAtomicInt(i, delta);
  }

  static public long inc(AtomicLong l) {
    return incAtomicLong(l);
  }

  static public long inc(AtomicLong l, long b) {
    return l.addAndGet(b);
  }

  static public int inc(IntVar v) {
    synchronized (v) {
      int val = v.get() + 1;
      v.set(val);
      return val;
    }
  }

  static public int inc(int i) {
    return i + 1;
  }

  static public long inc(long l) {
    return l + 1;
  }

  static public String tabToSingleSpace(String s) {
    return replace(s, '\t', ' ');
  }

  static public void readLocally(String progID, String varNames) {
    readLocally2(mc(), progID, varNames);
  }

  static public void readLocally(String varNames) {
    readLocally2(mc(), programID(), varNames);
  }

  static public void readLocally2(Object obj, String varNames) {
    readLocally2(obj, programID(), varNames);
  }

  static public int readLocally_stringLength;

  static public ThreadLocal<Boolean> readLocally2_allDynamic = new ThreadLocal();

  static public ThreadLocal readLocally2_classFinder = new ThreadLocal();

  static public void readLocally2(Object obj, String progID, String varNames) {
    try {
      boolean allDynamic = isTrue(getAndClearThreadLocal(readLocally2_allDynamic));
      for (String variableName : javaTokC(varNames)) {
        File textFile = new File(programDir(progID), variableName + ".text");
        String value = loadTextFile(textFile);
        if (value != null)
          set(main.class, variableName, value);
        else {
          File structureFile = new File(programDir(progID), variableName + ".structure");
          value = loadTextFile(structureFile);
          if (value == null) {
            File structureGZFile = new File(programDir(progID), variableName + ".structure.gz");
            if (!structureGZFile.isFile())
              return;
            InputStream fis = new FileInputStream(structureGZFile);
            try {
              GZIPInputStream gis = newGZIPInputStream(fis);
              InputStreamReader reader = new InputStreamReader(gis, "UTF-8");
              BufferedReader bufferedReader = new BufferedReader(reader);
              Object o = unstructure_tok(javaTokC_noMLS_onReader(bufferedReader), allDynamic, readLocally2_classFinder.get());
              readLocally_set(obj, variableName, o);
              return;
            } finally {
              _close(fis);
            }
          }
          readLocally_stringLength = l(value);
          if (nempty(value))
            readLocally_set(obj, variableName, unstructure(value, allDynamic, readLocally2_classFinder.get()));
        }
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public void readLocally_set(Object c, String varName, Object value) {
    Object oldValue = get(c, varName);
    if (oldValue instanceof List && !(oldValue instanceof ArrayList) && value != null) {
      value = synchroList((List) value);
    }
    set(c, varName, value);
  }

  static public Object nuObject(String className, Object... args) {
    try {
      return nuObject(classForName(className), args);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public <A> A nuObject(Class<A> c, Object... args) {
    try {
      if (args == null || args.length == 0)
        return nuObjectWithoutArguments(c);
      Constructor m = nuObject_findConstructor(c, args);
      makeAccessible(m);
      return (A) m.newInstance(args);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Constructor nuObject_findConstructor(Class c, Object... args) {
    for (Constructor m : getDeclaredConstructors_cached(c)) {
      if (!nuObject_checkArgs(m.getParameterTypes(), args, false))
        continue;
      return m;
    }
    throw fail("Constructor " + c.getName() + getClasses(args) + " not found" + (args.length == 0 && (c.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0 ? " - hint: it's a non-static class!" : ""));
  }

  static public boolean nuObject_checkArgs(Class[] types, Object[] args, boolean debug) {
    if (types.length != args.length) {
      if (debug)
        System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
      return false;
    }
    for (int i = 0; i < types.length; i++) if (!(args[i] == null || isInstanceX(types[i], args[i]))) {
      if (debug)
        System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
      return false;
    }
    return true;
  }

  static public void close_pcall(AutoCloseable c) {
    if (c != null) {
      try {
        c.close();
      } catch (Throwable __e) {
        pcallFail(__e);
      }
    }
  }

  static public <A, B> List<B> valuesList(Map<A, B> map) {
    return cloneListSynchronizingOn(values(map), map);
  }

  static public <A, B> List<B> valuesList(MultiMap<A, B> mm) {
    return mm == null ? emptyList() : concatLists(values(mm.data));
  }

  static public <A> void syncClear(Collection<A> c) {
    if (c != null)
      synchronized (collectionMutex(c)) {
        c.clear();
      }
  }

  static public void syncClear(Map map) {
    if (map != null)
      synchronized (collectionMutex(map)) {
        map.clear();
      }
  }

  static public void preCleanUp(Object c) {
    if (c instanceof Collection) {
      for (Object o : ((Collection) c)) preCleanUp(o);
      return;
    }
    callOpt(c, "licensed_off");
    setOpt_raw(c, "ping_anyActions", true);
    setOpt_raw(c, "cleaningUp_flag", true);
  }

  static public void innerCleanUp(Object c) {
    if (!isFalse(pcallOpt(c, "cleanMeUp")))
      for (String name : sorted(methodsStartingWith(c, "cleanMeUp_"))) try {
        callOpt(c, name);
      } catch (Throwable e) {
        print("Error cleaning up: " + programID(c));
        _handleException(e);
      }
  }

  static public void innerCleanUp() {
    innerCleanUp(mc());
  }

  static public Object pcallOpt(Object o, String method, Object... args) {
    try {
      return callOpt(o, method, args);
    } catch (Throwable __e) {
      pcallFail(__e);
    }
    return null;
  }

  static public List<Thread> registeredThreads(Object o) {
    Map<Thread, Boolean> map = (Map<Thread, Boolean>) (getOpt(o, "_registerThread_threads"));
    if (map == null)
      return ll();
    map.size();
    synchronized (map) {
      return asList(keys(map));
    }
  }

  static public List<Thread> registeredThreads() {
    _registerThread_threads.size();
    return asList(keys(_registerThread_threads));
  }

  static public void interruptThreads(Collection<Thread> threads) {
    for (Thread t : unnull(threads)) interruptThread(t);
  }

  static public void interruptThreads(Class mainClass) {
    interruptThreads(registeredThreads(mainClass));
  }

  static public void retireClassLoader(ClassLoader cl) {
    try {
      if (isJavaXClassLoader(cl))
        setOptAll(cl, "retired", true, "retiredMarker", new DefunctClassLoader());
    } catch (Throwable __e) {
      pcallFail(__e);
    }
  }

  static volatile public Concepts mainConcepts;

  static public Concepts db_mainConcepts() {
    if (mainConcepts == null)
      mainConcepts = newConceptsWithClassFinder(getDBProgramID());
    return mainConcepts;
  }

  static public void cleanMeUp_concepts() {
    if (db_mainConcepts() != null)
      db_mainConcepts().cleanMeUp();
  }

  static public <A, B> LinkedHashMap<A, B> cloneLinkedHashMap(Map<A, B> map) {
    return map == null ? new LinkedHashMap() : new LinkedHashMap(map);
  }

  static public Map<Class, Set<String>> allFields_cache = weakHashMap();

  static public Set<String> allFields(Object o) {
    if (o == null)
      return emptySet();
    Class _c = _getClass(o);
    Set<String> fields = allFields_cache.get(_c);
    if (fields == null)
      allFields_cache.put(_c, fields = asTreeSet(keys(getOpt_getFieldMap(o))));
    return fields;
  }

  static public int stdHash(Object a, String... fields) {
    if (a == null)
      return 0;
    int hash = getClassName(a).hashCode();
    for (String field : fields) hash = boostHashCombine(hash, hashCode(getOpt(a, field)));
    return hash;
  }

  static public Runnable wrapAsActivity(Object r) {
    if (r == null)
      return null;
    Runnable r2 = toRunnable(r);
    Object mod = dm_current_generic();
    if (mod == null)
      return r2;
    return new Runnable() {

      public void run() {
        try {
          AutoCloseable c = (AutoCloseable) (rcall("enter", mod));
          AutoCloseable __1 = c;
          try {
            r2.run();
          } finally {
            _close(__1);
          }
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "AutoCloseable c =  (AutoCloseable) (rcall enter(mod));\r\n    temp c;\r\n    r2.r...";
      }
    };
  }

  static public Map<Thread, Boolean> _registerThread_threads;

  static public Object _onRegisterThread;

  static public Thread _registerThread(Thread t) {
    if (_registerThread_threads == null)
      _registerThread_threads = newWeakHashMap();
    _registerThread_threads.put(t, true);
    vm_generalWeakSubMap("thread2mc").put(t, weakRef(mc()));
    callF(_onRegisterThread, t);
    return t;
  }

  static public void _registerThread() {
    _registerThread(Thread.currentThread());
  }

  static public Object iteratorFromFunction_endMarker = new Object();

  static public <A> IterableIterator<A> iteratorFromFunction_withEndMarker(final Object f) {
    class IFF extends IterableIterator<A> {

      public A a;

      public boolean have, done;

      public boolean hasNext() {
        getNext();
        return !done;
      }

      public A next() {
        getNext();
        if (done)
          throw fail();
        A _a = a;
        a = null;
        have = false;
        return _a;
      }

      public void getNext() {
        if (done || have)
          return;
        Object o = callF(f);
        if (o == iteratorFromFunction_endMarker) {
          done = true;
          return;
        }
        a = (A) o;
        have = true;
      }
    }
    ;
    return new IFF();
  }

  static public <A> IterableIterator<A> iteratorFromFunction_withEndMarker(final F0 f) {
    return iteratorFromFunction_withEndMarker_f0(f);
  }

  static public boolean isTrueOrYes(Object o) {
    return isTrueOpt(o) || o instanceof String && (eqicOneOf(((String) o), "1", "t", "true") || isYes(((String) o)));
  }

  static public <A, B> LinkedHashMap<A, B> asLinkedHashMap(Map<A, B> map) {
    if (map instanceof LinkedHashMap)
      return (LinkedHashMap) map;
    LinkedHashMap<A, B> m = new LinkedHashMap();
    if (map != null)
      synchronized (collectionMutex(map)) {
        m.putAll(map);
      }
    return m;
  }

  static public SimpleDateFormat simpleDateFormat_local(String format) {
    SimpleDateFormat sdf = new SimpleDateFormat(format);
    sdf.setTimeZone(localTimeZone());
    return sdf;
  }

  static public TreeSet<String> caseInsensitiveSet_treeSet() {
    return new TreeSet(caseInsensitiveComparator());
  }

  static public TreeSet<String> caseInsensitiveSet_treeSet(Collection<String> c) {
    return toCaseInsensitiveSet_treeSet(c);
  }

  static public String hijackPrint(Runnable r) {
    StringBuilder buf = new StringBuilder();
    Object old = interceptPrintInThisThread(new F1<String, Boolean>() {

      public Boolean get(String s) {
        buf.append(s);
        return false;
      }
    });
    try {
      {
        if (r != null)
          r.run();
      }
      return str(buf);
    } finally {
      interceptPrintInThisThread(old);
    }
  }

  static public <A, B> Map<A, List<B>> multiMapToMap(MultiMap<A, B> m) {
    return m == null ? null : m.data;
  }

  static public <A, B, C> IF2<A, B, C> if0ToIF2(IF0<C> f) {
    return f == null ? null : (a, b) -> f.get();
  }

  static public List<String> codeTokensOnly(List<String> tok) {
    int n = l(tok);
    List<String> l = emptyList(n / 2);
    for (int i = 1; i < n; i += 2) l.add(tok.get(i));
    return l;
  }

  static public int rjfind(List<String> tok, String in) {
    return rjfind(tok, 1, in);
  }

  static public int rjfind(List<String> tok, int startIdx, String in) {
    return rjfind(tok, startIdx, in, null);
  }

  static public int rjfind(List<String> tok, int startIdx, int endIndex, String in) {
    return rjfind(tok, startIdx, endIndex, in, null);
  }

  static public int rjfind(List<String> tok, String in, ITokCondition condition) {
    return rjfind(tok, 1, in, (Object) condition);
  }

  static public int rjfind(List<String> tok, String in, Object condition) {
    return rjfind(tok, 1, in, condition);
  }

  static public int rjfind(List<String> tok, int startIdx, String in, Object condition) {
    List<String> tokin = javaTok(in);
    jfind_preprocess(tokin);
    return rjfind(tok, startIdx, tokin, condition);
  }

  static public int rjfind(List<String> tok, int startIdx, int endIndex, String in, Object condition) {
    List<String> tokin = javaTok(in);
    jfind_preprocess(tokin);
    return rjfind(tok, startIdx, endIndex, tokin, condition);
  }

  static public int rjfind(List<String> tok, List<String> tokin) {
    return rjfind(tok, 1, tokin);
  }

  static public int rjfind(List<String> tok, int startIdx, List<String> tokin) {
    return rjfind(tok, startIdx, tokin, null);
  }

  static public int rjfind(List<String> tok, int startIdx, List<String> tokin, Object condition) {
    return rfindCodeTokens(tok, startIdx, false, toStringArray(codeTokensOnly(tokin)), condition);
  }

  static public int rjfind(List<String> tok, int startIdx, int endIndex, List<String> tokin, Object condition) {
    return rfindCodeTokens(tok, startIdx, endIndex, false, toStringArray(codeTokensOnly(tokin)), condition);
  }

  static public void clearTokens_maybeReTok(List<String> tok, int i, int j, boolean reTok) {
    clearTokens(tok, i, j);
    if (reTok)
      reTok(tok, i, j);
  }

  static public int findEndOfBracketPart(List<String> cnc, int i) {
    int j = i + 2, level = 1;
    while (j < cnc.size()) {
      if (eqOneOf(cnc.get(j), "{", "("))
        ++level;
      else if (eqOneOf(cnc.get(j), "}", ")"))
        --level;
      if (level == 0)
        return j + 1;
      ++j;
    }
    return cnc.size();
  }

  static public String toUpper(String s) {
    return s == null ? null : s.toUpperCase();
  }

  static public List<String> toUpper(Collection<String> s) {
    return allToUpper(s);
  }

  static public void replaceTokens(List<String> tok, IntRange r, String s) {
    replaceTokens(tok, r.start, r.end, s);
  }

  static public void replaceTokens(List<String> tok, int i, int j, String s) {
    clearAllTokens(tok, i + 1, j);
    tok.set(i, s);
  }

  static public void replaceTokens(List<String> tok, String s) {
    clearAllTokens(tok, 1, l(tok));
    tok.set(0, s);
  }

  static public ITokCondition tokCondition(IIntPred condition) {
    return condition == null ? null : (tok, nIdx) -> condition.get(nIdx);
  }

  static public String[] codeTokensAsStringArray(List<String> tok) {
    int n = max(0, (l(tok) - 1) / 2);
    String[] out = new String[n];
    for (int i = 0; i < n; i++) out[i] = tok.get(i * 2 + 1);
    return out;
  }

  static public <A> ArrayList<A> litlist(A... a) {
    ArrayList l = new ArrayList(a.length);
    for (A x : a) l.add(x);
    return l;
  }

  static public <A> List<A> replaceSublist(List<A> l, List<A> x, List<A> y) {
    if (x == null)
      return l;
    int i = 0;
    while (true) {
      i = indexOfSubList(l, x, i);
      if (i < 0)
        break;
      replaceSublist(l, i, i + l(x), y);
      i += l(y);
    }
    return l;
  }

  static public <A> List<A> replaceSublist(List<A> l, int fromIndex, int toIndex, List<A> y) {
    int n = y.size(), toIndex_new = fromIndex + n;
    if (toIndex_new < toIndex) {
      removeSubList(l, toIndex_new, toIndex);
      copyListPart(y, 0, l, fromIndex, n);
    } else {
      copyListPart(y, 0, l, fromIndex, toIndex - fromIndex);
      if (toIndex_new > toIndex)
        l.addAll(toIndex, subList(y, toIndex - fromIndex));
    }
    return l;
  }

  static public <A> List<A> replaceSublist(List<A> l, IntRange r, List<A> y) {
    return replaceSublist(l, r.start, r.end, y);
  }

  static public int tok_script_findEndOfType(List<String> tok) {
    return tok_script_findEndOfType(tok, 1);
  }

  static public int tok_script_findEndOfType(List<String> tok, int i) {
    while (licensed()) {
      if (eqGet(tok, i + 2, ".") && isIdentifier(get(tok, i + 4))) {
        i += 4;
        continue;
      }
      if (eqGet(tok, i + 2, "<") && isIdentifier(get(tok, i + 4))) {
        i = findEndOfTypeArgs(tok, i + 2) - 1;
        continue;
      }
      if (eqGet(tok, i + 2, "[")) {
        i = findEndOfBracketPart2(tok, i + 2) - 1;
        continue;
      }
      break;
    }
    return i + 2;
  }

  static public String joinSubList_cToC(List<String> l, int i, int j) {
    return joinSubList(l, i | 1, j & ~1);
  }

  static public void tokSet_withReTok(List<String> tok, int i, String t) {
    put(tok, i, t);
    reTok(tok, i, i + 1);
  }

  static public <A, B> Map<A, B> synchronizedMRUCache(int maxSize) {
    return synchroMap(new MRUCache(maxSize));
  }

  static public boolean checkTokCondition(Object condition, List<String> tok, int i) {
    if (condition instanceof TokCondition)
      return ((TokCondition) condition).get(tok, i);
    return checkCondition(condition, tok, i);
  }

  static public <A> void replaceCollection(Collection<A> dest, Collection<A> src) {
    if (dest == src)
      return;
    dest.clear();
    if (src != null)
      dest.addAll(src);
  }

  static public void replaceListPart(List l, int i, int j, List l2) {
    replaceSublist(l, i, j, l2);
  }

  static public boolean containsNewLines(String s) {
    return containsNewLine(s);
  }

  static public String jlabel_textAsHTML_center(String text) {
    return "<html><div style=\"text-align: center;\">" + replace(htmlencode2(text), "\n", "<br>") + "</div></html>";
  }

  static public boolean isInstance(Class type, Object arg) {
    return type.isInstance(arg);
  }

  static public String renderVars_str(Object... params) {
    List<String> l = new ArrayList();
    int i = 0;
    if (odd(l(params))) {
      l.add(strOrNull(first(params)));
      ++i;
    }
    for (; i + 1 < l(params); i += 2) l.add(params[i] + "=" + params[i + 1]);
    return trim(joinWithComma(l));
  }

  static public <A> A[] arrayOfSameType(A[] a, int n) {
    return newObjectArrayOfSameType(a, n);
  }

  static public <A> A[] makeArray(Class<A> type, int n) {
    return (A[]) Array.newInstance(type, n);
  }

  static public boolean containsNulls(Collection c) {
    return contains(c, null);
  }

  static public Map similarEmptyMap(Map m) {
    if (m instanceof TreeMap)
      return new TreeMap(((TreeMap) m).comparator());
    if (m instanceof LinkedHashMap)
      return new LinkedHashMap();
    return new HashMap();
  }

  static public Map similarEmptyMap(Iterable m) {
    if (m instanceof TreeSet)
      return new TreeMap(((TreeSet) m).comparator());
    if (m instanceof LinkedHashSet)
      return new LinkedHashMap();
    return new HashMap();
  }

  static public <A> boolean addIfNotNull(Collection<A> l, A a) {
    return a != null && l != null & l.add(a);
  }

  static public <A> void addIfNotNull(MultiSet<A> ms, A a) {
    if (a != null && ms != null)
      ms.add(a);
  }

  static public <A> A[] typedArray(Class<A> c, int n) {
    return (A[]) Array.newInstance(c, n);
  }

  static public int lCommonPrefix(String a, String b) {
    int i = 0, n = Math.min(l(a), l(b));
    while (i < n && a.charAt(i) == b.charAt(i)) ++i;
    return i;
  }

  static public <A> List<A> takeFirst(List<A> l, int n) {
    return l(l) <= n ? l : newSubListOrSame(l, 0, n);
  }

  static public <A> List<A> takeFirst(int n, List<A> l) {
    return takeFirst(l, n);
  }

  static public String takeFirst(int n, String s) {
    return substring(s, 0, n);
  }

  static public String takeFirst(String s, int n) {
    return substring(s, 0, n);
  }

  static public CharSequence takeFirst(int n, CharSequence s) {
    return subCharSequence(s, 0, n);
  }

  static public <A> List<A> takeFirst(int n, Iterator<A> it) {
    if (it == null)
      return null;
    List l = new ArrayList();
    for (int _repeat_0 = 0; _repeat_0 < n; _repeat_0++) {
      if (it.hasNext())
        l.add(it.next());
      else
        break;
    }
    return l;
  }

  static public <A> List<A> takeFirst(int n, Iterable<A> i) {
    if (i == null)
      return null;
    return i == null ? null : takeFirst(n, i.iterator());
  }

  static public <A> List<A> takeFirst(int n, IterableIterator<A> i) {
    return takeFirst(n, (Iterator<A>) i);
  }

  static public int[] takeFirst(int n, int[] a) {
    return takeFirstOfIntArray(n, a);
  }

  static public short[] takeFirst(int n, short[] a) {
    return takeFirstOfShortArray(n, a);
  }

  static public byte[] takeFirst(int n, byte[] a) {
    return takeFirstOfByteArray(n, a);
  }

  static public byte[] takeFirst(byte[] a, int n) {
    return takeFirstOfByteArray(n, a);
  }

  static public double[] takeFirst(int n, double[] a) {
    return takeFirstOfDoubleArray(n, a);
  }

  static public double[] takeFirst(double[] a, int n) {
    return takeFirstOfDoubleArray(n, a);
  }

  static public <A, B> Map<A, B> takeFirst(int n, Map<A, B> map) {
    return takeFirstFromMap(n, map);
  }

  static public void upgradeJavaXAndRestart() {
    run("#1001639");
    restart();
    sleep();
  }

  static public int mod(int n, int m) {
    return (n % m + m) % m;
  }

  static public long mod(long n, long m) {
    return (n % m + m) % m;
  }

  static public BigInteger mod(BigInteger n, int m) {
    return n.mod(bigint(m));
  }

  static public double mod(double n, double m) {
    return (n % m + m) % m;
  }

  static public String hopeningTag(String tag, Map params) {
    return hopeningTag(tag, mapToParams(params));
  }

  static public String hopeningTag(String tag, Object... params) {
    StringBuilder buf = new StringBuilder();
    buf.append("<" + tag);
    params = unrollParams(params);
    for (int i = 0; i < l(params); i += 2) {
      String name = (String) get(params, i);
      Object val = get(params, i + 1);
      if (nempty(name) && val != null) {
        if (eqOneOf(val, html_valueLessParam(), true))
          buf.append(" " + name);
        else {
          String s = str(val);
          if (!empty(s))
            buf.append(" " + name + "=" + htmlQuote(s));
        }
      }
    }
    buf.append(">");
    return str(buf);
  }

  static public String joinStrings(String sep, Object... strings) {
    return joinStrings(sep, Arrays.asList(strings));
  }

  static public String joinStrings(String sep, Iterable strings) {
    StringBuilder buf = new StringBuilder();
    for (Object o : unnull(strings)) {
      String s = strOrNull(o);
      if (nempty(s)) {
        if (nempty(buf))
          buf.append(sep);
        buf.append(s);
      }
    }
    return str(buf);
  }

  static public String trim(String s) {
    return s == null ? null : s.trim();
  }

  static public String trim(StringBuilder buf) {
    return buf.toString().trim();
  }

  static public String trim(StringBuffer buf) {
    return buf.toString().trim();
  }

  static public String formatWithThousandsSeparator(long l) {
    return NumberFormat.getInstance(new Locale("en_US")).format(l);
  }

  static public Set<String> getPlural_specials = litciset("sheep", "fish");

  static public String getPlural(String s) {
    if (contains(getPlural_specials, s))
      return s;
    if (ewic(s, "y"))
      return dropSuffixIgnoreCase("y", s) + "ies";
    if (ewicOneOf(s, "ss", "ch"))
      return s + "es";
    if (ewic(s, "s"))
      return s;
    return s + "s";
  }

  static public int typeConversionScoreWithUnboxing(Class a, Class b) {
    int score = typeConversionScore(a, b);
    if (score == 0 || score == 1)
      return score;
    Class aPrim = boxedToPrimitiveType(a);
    if (aPrim != null) {
      int score2 = typeConversionScore(aPrim, b);
      if (score2 == 0)
        return 1;
      else if (score2 != Integer.MAX_VALUE)
        return score2;
    }
    return score;
  }

  static public boolean isIPv4(String s) {
    return s != null && l(javaTokC(s)) == 7 && jfind(javaTok(s), "<int>.<int>.<int>.<int>") == 1;
  }

  static public <A> A[] newObjectArrayOfSameType(A[] a) {
    return newObjectArrayOfSameType(a, a.length);
  }

  static public <A> A[] newObjectArrayOfSameType(A[] a, int n) {
    return (A[]) Array.newInstance(a.getClass().getComponentType(), n);
  }

  static public <A> A singletonOpt(Collection<A> l) {
    return l(l) == 1 ? first(l) : null;
  }

  static public <A> A singletonOpt(A[] l) {
    return l(l) == 1 ? first(l) : null;
  }

  static public List<Method> findFunctionalInterfaceMethods(Class intrface) {
    if (!isInterface(intrface))
      return null;
    List<Method> l = new ArrayList();
    _MethodCache cache = getMethodCache(intrface);
    Class c = intrface;
    do {
      for (Method m : c.getDeclaredMethods()) {
        String name = m.getName();
        var args = m.getParameterTypes();
        if (m.getDeclaringClass() != Object.class && !m.isDefault() && !isStaticMethod(m) && !m.isSynthetic() && !(eq(name, "equals") && l(args) == 1 && args[0] == Object.class))
          l.add(m);
      }
      c = c.getSuperclass();
    } while (c != null);
    return l;
  }

  static public IterableIterator emptyIterableIterator_instance = new IterableIterator() {

    public Object next() {
      throw fail();
    }

    public boolean hasNext() {
      return false;
    }
  };

  static public <A> IterableIterator<A> emptyIterableIterator() {
    return emptyIterableIterator_instance;
  }

  static public List<String> javaTokC(String s) {
    if (s == null)
      return null;
    int l = s.length();
    ArrayList<String> tok = new ArrayList();
    int i = 0;
    while (i < l) {
      int j = i;
      char c, d;
      while (j < l) {
        c = s.charAt(j);
        d = j + 1 >= l ? '\0' : s.charAt(j + 1);
        if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
          ++j;
        else if (c == '/' && d == '*') {
          do ++j; while (j < l && !s.substring(j, Math.min(j + 2, l)).equals("*/"));
          j = Math.min(j + 2, l);
        } else if (c == '/' && d == '/') {
          do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0);
        } else
          break;
      }
      i = j;
      if (i >= l)
        break;
      c = s.charAt(i);
      d = i + 1 >= l ? '\0' : s.charAt(i + 1);
      if (c == '\'' || c == '"') {
        char opener = c;
        ++j;
        while (j < l) {
          if (s.charAt(j) == opener || s.charAt(j) == '\n') {
            ++j;
            break;
          } else if (s.charAt(j) == '\\' && j + 1 < l)
            j += 2;
          else
            ++j;
        }
      } else if (Character.isJavaIdentifierStart(c))
        do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || "'".indexOf(s.charAt(j)) >= 0));
      else if (Character.isDigit(c)) {
        do ++j; while (j < l && Character.isDigit(s.charAt(j)));
        if (j < l && s.charAt(j) == 'L')
          ++j;
      } else if (c == '[' && d == '[') {
        do ++j; while (j + 1 < l && !s.substring(j, j + 2).equals("]]"));
        j = Math.min(j + 2, l);
      } else if (c == '[' && d == '=' && i + 2 < l && s.charAt(i + 2) == '[') {
        do ++j; while (j + 2 < l && !s.substring(j, j + 3).equals("]=]"));
        j = Math.min(j + 3, l);
      } else
        ++j;
      tok.add(javaTok_substringC(s, i, j));
      i = j;
    }
    return tok;
  }

  static public <A> A popLast(List<A> l) {
    return liftLast(l);
  }

  static public <A> List<A> popLast(int n, List<A> l) {
    return liftLast(n, l);
  }

  static public String stringIf(boolean b, String s) {
    return stringIfTrue(b, s);
  }

  static public String stringIf(String s, boolean b) {
    return stringIf(b, s);
  }

  static public String actualMCDollar() {
    return actualMC().getName() + "$";
  }

  static public boolean isJavaXClassName(String s) {
    return isJavaXClassName(s, "main$");
  }

  static public boolean isJavaXClassName(String s, String mcDollar) {
    return startsWithOneOf(s, mcDollar, "main$", "loadableUtils.", "x30", "userCode.");
  }

  static public void quoteToPrintWriter(String s, PrintWriter out) {
    if (s == null) {
      out.print("null");
      return;
    }
    out.print('"');
    int l = s.length();
    for (int i = 0; i < l; i++) {
      char c = s.charAt(i);
      if (c == '\\' || c == '"') {
        out.print('\\');
        out.print(c);
      } else if (c == '\r')
        out.print("\\r");
      else if (c == '\n')
        out.print("\\n");
      else if (c == '\0')
        out.print("\\0");
      else
        out.print(c);
    }
    out.print('"');
  }

  static public boolean isSubClassOf(Class a, Class b) {
    return isSubclass(a, b);
  }

  static public boolean isSyntheticOrAnonymous(Class c) {
    return c != null && (c.isSynthetic() || isAnonymousClassName(c.getName()));
  }

  static public <A> List<A> unwrapSynchronizedList(List<A> l) {
    if (l instanceof SynchronizedList)
      return ((SynchronizedList) l).list;
    if (eqOneOf(className(l), "java.util.Collections$SynchronizedList", "java.util.Collections$SynchronizedRandomAccessList"))
      return (List) get_raw(l, "list");
    return l;
  }

  static public String shortDynClassNameForStructure(Object o) {
    if (o instanceof DynamicObject && ((DynamicObject) o).className != null)
      return ((DynamicObject) o).className;
    if (o == null)
      return null;
    Class c = o instanceof Class ? (Class) o : o.getClass();
    String name = c.getName();
    return name.startsWith("dyn.") ? classNameToVM(name) : shortenClassName(name);
  }

  static public boolean isPersistableClass(Class c) {
    String name = c.getName();
    if (isSubtypeOf(c, TransientObject.class))
      return false;
    if (isAnonymousClassName(name))
      return false;
    if (isBoxedType(c))
      return true;
    if (isArrayType(c))
      return true;
    if (c == Class.class || c == String.class || c == File.class || c == Color.class)
      return true;
    if (name.startsWith("java.util.Collections$Synchronized"))
      return true;
    if (hasThisDollarFields(c))
      return hasSingleArgumentConstructor(c);
    else
      return getDefaultConstructor(c) != null;
  }

  static public Constructor getDefaultConstructor(Class c) {
    if (c != null)
      for (Constructor m : getDeclaredConstructors_cached(c)) if (empty(m.getParameterTypes()))
        return m;
    return null;
  }

  static public int countDots(String s) {
    int n = l(s), count = 0;
    for (int i = 0; i < n; i++) if (s.charAt(i) == '.')
      ++count;
    return count;
  }

  static public boolean isCIMap_gen(Map map) {
    return map instanceof TreeMap && className(((TreeMap) map).comparator()).contains("CIComp");
  }

  static public <A, B> Map<A, B> unwrapSynchronizedMap(Map<A, B> map) {
    if (eqOneOf(shortClassName(map), "SynchronizedMap", "SynchronizedSortedMap", "SynchronizedNavigableMap"))
      return (Map) get_raw(map, "m");
    return map;
  }

  static public String renderVars(Object... params) {
    return renderVars_str(params);
  }

  static public boolean isCISet_gen(Iterable<String> l) {
    return l instanceof TreeSet && className(((TreeSet) l).comparator()).contains("CIComp");
  }

  static public String quoteCharacter(char c) {
    if (c == '\'')
      return "'\\''";
    if (c == '\\')
      return "'\\\\'";
    if (c == '\r')
      return "'\\r'";
    if (c == '\n')
      return "'\\n'";
    if (c == '\t')
      return "'\\t'";
    return "'" + c + "'";
  }

  static public String boolArrayToHex(boolean[] a) {
    return bytesToHex(boolArrayToBytes(a));
  }

  static public String shortArrayToHex_bigEndian(short[] a) {
    return bytesToHex(byteArrayFromShorts_bigEndian(a));
  }

  static public String longArrayToHex_bigEndian(long[] a) {
    return bytesToHex(byteArrayFromLongs_bigEndian(a));
  }

  static public Pair<Class, Integer> arrayTypeAndDimensions(Object o) {
    return arrayTypeAndDimensions(_getClass(o));
  }

  static public Pair<Class, Integer> arrayTypeAndDimensions(Class c) {
    if (c == null || !c.isArray())
      return null;
    Class elem = c.getComponentType();
    if (elem.isArray())
      return mapPairB(arrayTypeAndDimensions(elem), dim -> dim + 1);
    return pair(elem, 1);
  }

  static public int stdcompare(Number a, Number b) {
    return cmp(a, b);
  }

  static public int stdcompare(String a, String b) {
    return cmp(a, b);
  }

  static public int stdcompare(long a, long b) {
    return a < b ? -1 : a > b ? 1 : 0;
  }

  static public int stdcompare(Object a, Object b) {
    return cmp(a, b);
  }

  static public Map<Class, Field[]> getDeclaredFields_cache = newDangerousWeakHashMap();

  static public Field[] getDeclaredFields_cached(Class c) {
    Field[] fields;
    synchronized (getDeclaredFields_cache) {
      fields = getDeclaredFields_cache.get(c);
      if (fields == null) {
        getDeclaredFields_cache.put(c, fields = c.getDeclaredFields());
        for (Field f : fields) makeAccessible(f);
      }
    }
    return fields;
  }

  static public Method findInstanceMethod(Class c, String method, Object... args) {
    while (c != null) {
      for (Method m : c.getDeclaredMethods()) if (m.getName().equals(method) && findMethod_checkArgs(m, args, false))
        return m;
      c = c.getSuperclass();
    }
    return null;
  }

  static public Set<Field> fieldObjectsInFieldOrder(Class c, Set<Field> fields) {
    try {
      var byName = mapToKey(f -> f.getName(), fields);
      LinkedHashSet<Field> out = new LinkedHashSet();
      for (String name : unnullForIteration(getFieldOrder(c))) {
        Field f = byName.get(name);
        if (f != null) {
          byName.remove(name);
          out.add(f);
        }
      }
      addAll(out, fields);
      return out;
    } catch (Throwable __0) {
      printStackTrace(__0);
      return fields;
    }
  }

  static public String quoted(Object o) {
    return quote(o);
  }

  static public String quoted(String s) {
    return quote(s);
  }

  static public int identityHashCode(Object o) {
    return System.identityHashCode(o);
  }

  static public IMeta initMetaOfJComponent(JComponent c) {
    if (c == null)
      return null;
    IMeta meta = (IMeta) (c.getClientProperty(IMeta.class));
    if (meta == null)
      c.putClientProperty(IMeta.class, meta = new Meta());
    return meta;
  }

  static public boolean zipFileContains_falseOnError(File inZip, String fileName) {
    try {
      return zipFileContains(inZip, fileName);
    } catch (Throwable e) {
      return false;
    }
  }

  static public File javaHome() {
    return envJavaHome();
  }

  static public File javaHome(String sub) {
    return newFile(envJavaHome(), sub);
  }

  static public File jigsawModuleFile(String moduleName) {
    return jigsawModuleFile(moduleName, javaHome());
  }

  static public File jigsawModuleFile(String moduleName, File javaHome) {
    return new File(javaHome, "jmods/" + moduleName + ".jmod");
  }

  static public Object dm_current_generic() {
    return getWeakRef(dm_current_generic_tl().get());
  }

  static public Object rcall(String method, Object o, Object... args) {
    return call_withVarargs(o, method, args);
  }

  static public Runnable rPcall(Runnable r) {
    return r == null ? null : () -> {
      try {
        r.run();
      } catch (Throwable __e) {
        pcallFail(__e);
      }
    };
  }

  static public String programID() {
    return getProgramID();
  }

  static public String programID(Object o) {
    return getProgramID(o);
  }

  static public <A> List<A> newSubList(List<A> l, int startIndex, int endIndex) {
    return cloneList(subList(l, startIndex, endIndex));
  }

  static public <A> List<A> newSubList(List<A> l, int startIndex) {
    return cloneList(subList(l, startIndex));
  }

  static public Charset utf8charset_cache;

  static public Charset utf8charset() {
    if (utf8charset_cache == null)
      utf8charset_cache = utf8charset_load();
    return utf8charset_cache;
  }

  static public Charset utf8charset_load() {
    return Charset.forName("UTF-8");
  }

  static public void setDynObjectValue(DynamicObject o, String field, Object value) {
    dynamicObject_setRawFieldValue(o, field, value);
  }

  static public List<String> mapToLines(Map map) {
    List<String> l = new ArrayList();
    for (Object key : keys(map)) l.add(str(key) + " = " + str(map.get(key)));
    return l;
  }

  static public String mapToLines(Map map, Object f) {
    return lines(map(map, f));
  }

  static public String mapToLines(Object f, Map map) {
    return lines(map(map, f));
  }

  static public String mapToLines(Object f, Iterable l) {
    return lines(map(f, l));
  }

  static public <A> String mapToLines(Iterable<A> l, IF1<A, String> f) {
    return mapToLines((Object) f, l);
  }

  static public <A> String mapToLines(IF1<A, String> f, Iterable<A> l) {
    return mapToLines((Object) f, l);
  }

  static public <A, B> String mapToLines(Map<A, B> map, IF2<A, B, String> f) {
    return lines(map(map, f));
  }

  static public <A> String mapToLines(IF1<A, String> f, A data1, A... moreData) {
    return lines(map(f, data1, moreData));
  }

  static public <A> List<A> wrapAsList(A[] a) {
    return wrapArrayAsList(a);
  }

  static public String fromLines(Iterable lines) {
    StringBuilder buf = new StringBuilder();
    if (lines != null)
      for (Object line : lines) buf.append(str(line)).append('\n');
    return buf.toString();
  }

  static public String fromLines(String... lines) {
    return fromLines(asList(lines));
  }

  static public IterableIterator<String> toLines(File f) {
    return linesFromFile(f);
  }

  static public List<String> toLines(String s) {
    List<String> lines = new ArrayList<String>();
    if (s == null)
      return lines;
    int start = 0;
    while (true) {
      int i = toLines_nextLineBreak(s, start);
      if (i < 0) {
        if (s.length() > start)
          lines.add(s.substring(start));
        break;
      }
      lines.add(s.substring(start, i));
      if (s.charAt(i) == '\r' && i + 1 < s.length() && s.charAt(i + 1) == '\n')
        i += 2;
      else
        ++i;
      start = i;
    }
    return lines;
  }

  static public int toLines_nextLineBreak(String s, int start) {
    int n = s.length();
    for (int i = start; i < n; i++) {
      char c = s.charAt(i);
      if (c == '\r' || c == '\n')
        return i;
    }
    return -1;
  }

  static public List<String> linesFromFile_list(File f) {
    try {
      return asList_closeIterator(linesFromFile(f));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public List<String> linesFromFile_list(String path) {
    return linesFromFile_list(newFile(path));
  }

  static public Object costCenter() {
    return mc();
  }

  static public boolean isSubclassOf(Class a, Class b) {
    return isSubclass(a, b);
  }

  static public String appendColonIfNempty(String s) {
    return empty(s) ? "" : s + ": ";
  }

  static public boolean isEmpty(Collection c) {
    return c == null || c.isEmpty();
  }

  static public boolean isEmpty(File f) {
    return f == null || f.length() == 0;
  }

  static public boolean isEmpty(CharSequence s) {
    return s == null || s.length() == 0;
  }

  static public boolean isEmpty(Object[] a) {
    return a == null || a.length == 0;
  }

  static public boolean isEmpty(byte[] a) {
    return a == null || a.length == 0;
  }

  static public boolean isEmpty(Map map) {
    return map == null || map.isEmpty();
  }

  static public boolean isEmpty(AppendableChain c) {
    return c == null;
  }

  static public boolean isEmpty(IntSize l) {
    return l == null || l.size() == 0;
  }

  static public int incAtomicInt(AtomicInteger i) {
    return i.incrementAndGet();
  }

  static public int incAtomicInt(AtomicInteger i, int delta) {
    return i.addAndGet(delta);
  }

  static public <A> A getAndClearThreadLocal(ThreadLocal<A> tl) {
    A a = tl.get();
    tl.set(null);
    return a;
  }

  static public File programDir_mine;

  static public File programDir() {
    return programDir(getProgramID());
  }

  static public File programDir(String snippetID) {
    boolean me = sameSnippetID(snippetID, programID());
    if (programDir_mine != null && me)
      return programDir_mine;
    File dir = new File(javaxDataDir(), formatSnippetIDOpt(snippetID));
    if (me) {
      String c = caseID();
      if (nempty(c))
        dir = newFile(dir, c);
    }
    return dir;
  }

  static public File programDir(String snippetID, String subPath) {
    return new File(programDir(snippetID), subPath);
  }

  static public String loadTextFile(String fileName) {
    return loadTextFile(fileName, null);
  }

  static public String loadTextFile(File f, String defaultContents) {
    return loadTextFile(f, defaultContents, "UTF-8");
  }

  static public String loadTextFile(File f, String defaultContents, String encoding) {
    try {
      checkFileNotTooBigToRead(f);
      if (f == null || !f.exists())
        return defaultContents;
      FileInputStream fileInputStream = new FileInputStream(f);
      InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, encoding);
      return loadTextFile(inputStreamReader);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static String loadTextFile(File fileName) {
    return loadTextFile(fileName, null);
  }

  static public String loadTextFile(String fileName, String defaultContents) {
    return fileName == null ? defaultContents : loadTextFile(newFile(fileName), defaultContents);
  }

  static public String loadTextFile(Reader reader) throws IOException {
    StringBuilder builder = new StringBuilder();
    try {
      char[] buffer = new char[1024];
      int n;
      while (-1 != (n = reader.read(buffer))) builder.append(buffer, 0, n);
    } finally {
      reader.close();
    }
    return str(builder);
  }

  static public GZIPInputStream newGZIPInputStream(File f) {
    return gzInputStream(f);
  }

  static public GZIPInputStream newGZIPInputStream(InputStream in) {
    return gzInputStream(in);
  }

  static public Object unstructure(String text) {
    return unstructure(text, false);
  }

  static public Object unstructure(String text, boolean allDynamic) {
    return unstructure(text, allDynamic, null);
  }

  static public Object unstructure(String text, IF1<String, Class> classFinder) {
    return unstructure(text, false, classFinder);
  }

  static public int structure_internStringsLongerThan = 50;

  static public int unstructure_unquoteBufSize = 100;

  static public int unstructure_tokrefs;

  abstract static public class unstructure_Receiver {

    abstract public void set(Object o);
  }

  static public Object unstructure(String text, boolean allDynamic, Object classFinder) {
    if (text == null)
      return null;
    return unstructure_tok(javaTokC_noMLS_iterator(text), allDynamic, classFinder);
  }

  static public Object unstructure_reader(BufferedReader reader) {
    return unstructure_tok(javaTokC_noMLS_onReader(reader), false, null);
  }

  public interface unstructure_Handler {

    public void parse(int refID, int tokIndex, unstructure_Receiver out);
  }

  static public class Unstructurer {

    final public Unstructurer setTok(Producer<String> tok) {
      return tok(tok);
    }

    public Unstructurer tok(Producer<String> tok) {
      this.tok = tok;
      return this;
    }

    final public Producer<String> getTok() {
      return tok();
    }

    public Producer<String> tok() {
      return tok;
    }

    public Producer<String> tok;

    final public Unstructurer setAllDynamic(boolean allDynamic) {
      return allDynamic(allDynamic);
    }

    public Unstructurer allDynamic(boolean allDynamic) {
      this.allDynamic = allDynamic;
      return this;
    }

    final public boolean getAllDynamic() {
      return allDynamic();
    }

    public boolean allDynamic() {
      return allDynamic;
    }

    public boolean allDynamic = false;

    public int i = -1;

    public Object classFinder;

    public String mcDollar = actualMCDollar();

    public Unstructurer classFinder(Object _classFinder) {
      classFinder = _classFinder != null ? _classFinder : _defaultClassFinder();
      return this;
    }

    public HashMap<Integer, Object> refs = new HashMap();

    public HashMap<Integer, Object> tokrefs = new HashMap();

    public HashSet<String> concepts = new HashSet();

    public List<Runnable> stack = new ArrayList();

    public Map<String, String> baseClassMap = new HashMap();

    public HashMap<Class, Constructor> innerClassConstructors = new HashMap();

    public String curT;

    public char[] unquoteBuf = new char[unstructure_unquoteBufSize];

    final public HashMap<String, Object> handlers = new HashMap();

    public Unstructurer() {
      try {
        Class mc = (Class) (callF(classFinder, "<main>"));
        if (mc != null)
          mcDollar = mc.getName() + "$";
      } catch (Throwable __e) {
        pcallFail(__e);
      }
      makeHandlers();
    }

    public void makeHandlers() {
      unstructure_Handler h;
      handlers.put("bigint", (unstructure_Handler) (refID, tokIndex, out) -> out.set(parseBigInt()));
      handlers.put("d", (unstructure_Handler) (refID, tokIndex, out) -> out.set(parseDouble()));
      handlers.put("fl", (unstructure_Handler) (refID, tokIndex, out) -> out.set(parseFloat()));
      handlers.put("sh", (unstructure_Handler) (refID, tokIndex, out) -> {
        consume();
        String t = tpp();
        if (t.equals("-")) {
          t = tpp();
          out.set((short) (-parseInt(t)));
          return;
        }
        out.set((short) parseInt(t));
      });
      handlers.put("enum", (unstructure_Handler) (refID, tokIndex, out) -> {
        consume();
        String t = tpp();
        assertTrue(isJavaIdentifier(t));
        String fullClassName = mcDollar + t;
        Class _c = findAClass(fullClassName);
        if (_c == null)
          throw fail("Enum class not found: " + fullClassName);
        int ordinal = parseInt(tpp());
        out.set(_c.getEnumConstants()[ordinal]);
      });
      handlers.put("false", h = (unstructure_Handler) (refID, tokIndex, out) -> {
        consume();
        out.set(false);
      });
      handlers.put("f", h);
      handlers.put("true", h = (unstructure_Handler) (refID, tokIndex, out) -> {
        consume();
        out.set(true);
      });
      handlers.put("t", h);
      handlers.put("{", (unstructure_Handler) (refID, tokIndex, out) -> parseMap(out));
      handlers.put("[", (unstructure_Handler) (refID, tokIndex, out) -> {
        ArrayList l = new ArrayList();
        if (refID >= 0)
          refs.put(refID, l);
        this.parseList(l, out);
      });
      handlers.put("bitset", (unstructure_Handler) (refID, tokIndex, out) -> parseBitSet(out));
      handlers.put("array", h = (unstructure_Handler) (refID, tokIndex, out) -> parseArray(out));
      handlers.put("intarray", h);
      handlers.put("dblarray", h);
      handlers.put("floatarray", h);
      handlers.put("shortarray", (unstructure_Handler) (refID, tokIndex, out) -> {
        consume();
        String hex = trivialUnquote(tpp());
        out.set(shortArrayFromBytes(hexToBytes(hex)));
      });
      handlers.put("longarray", (unstructure_Handler) (refID, tokIndex, out) -> {
        consume();
        String hex = trivialUnquote(tpp());
        out.set(longArrayFromBytes(hexToBytes(hex)));
      });
    }

    public Class findAClass(String fullClassName) {
      try {
        return classFinder != null ? (Class) callF(classFinder, fullClassName) : findClass_fullName(fullClassName);
      } catch (Throwable __e) {
        return null;
      }
    }

    public String unquote(String s) {
      return unquoteUsingCharArray(s, unquoteBuf);
    }

    public String t() {
      return curT;
    }

    public String tpp() {
      String t = curT;
      consume();
      return t;
    }

    public void parse(final unstructure_Receiver out) {
      String t = t();
      int refID;
      if (structure_isMarker(t, 0, l(t))) {
        refID = parseInt(t.substring(1));
        consume();
      } else
        refID = -1;
      final int tokIndex = i;
      parse_inner(refID, tokIndex, new unstructure_Receiver() {

        public void set(Object o) {
          if (refID >= 0)
            refs.put(refID, o);
          if (o != null)
            tokrefs.put(tokIndex, o);
          out.set(o);
        }
      });
    }

    public void parse_inner(int refID, int tokIndex, unstructure_Receiver out) {
      String t = t();
      Object handler = handlers.get(t);
      if (handler instanceof unstructure_Handler) {
        ((unstructure_Handler) handler).parse(refID, tokIndex, out);
        return;
      }
      Class c = (Class) handler;
      if (c == null) {
        if (t.startsWith("\"")) {
          String s = internIfLongerThan(unquote(tpp()), structure_internStringsLongerThan);
          out.set(s);
          return;
        }
        if (t.startsWith("'")) {
          out.set(unquoteCharacter(tpp()));
          return;
        }
        if (t.equals("-")) {
          consume();
          t = tpp();
          out.set(isLongConstant(t) ? (Object) (-parseLong(t)) : (Object) (-parseInt(t)));
          return;
        }
        if (isInteger(t) || isLongConstant(t)) {
          consume();
          if (isLongConstant(t)) {
            out.set(parseLong(t));
            return;
          }
          long l = parseLong(t);
          boolean isInt = l == (int) l;
          out.set(isInt ? (Object) Integer.valueOf((int) l) : (Object) Long.valueOf(l));
          return;
        }
        if (t.equals("-")) {
          consume();
          t = tpp();
          out.set(isLongConstant(t) ? (Object) (-parseLong(t)) : (Object) (-parseInt(t)));
          return;
        }
        if (isInteger(t) || isLongConstant(t)) {
          consume();
          if (isLongConstant(t)) {
            out.set(parseLong(t));
            return;
          }
          long l = parseLong(t);
          boolean isInt = l == (int) l;
          out.set(isInt ? (Object) Integer.valueOf((int) l) : (Object) Long.valueOf(l));
          return;
        }
        if (t.equals("File")) {
          consume();
          File f = new File(unquote(tpp()));
          out.set(f);
          return;
        }
        if (t.startsWith("r") && isInteger(t.substring(1))) {
          consume();
          int ref = Integer.parseInt(t.substring(1));
          Object o = refs.get(ref);
          if (o == null)
            warn("unsatisfied back reference " + ref);
          out.set(o);
          return;
        }
        if (t.startsWith("t") && isInteger(t.substring(1))) {
          consume();
          int ref = Integer.parseInt(t.substring(1));
          Object o = tokrefs.get(ref);
          if (o == null)
            warn("unsatisfied token reference " + ref + " at " + tokIndex);
          out.set(o);
          return;
        }
        if (t.equals("hashset")) {
          parseHashSet(out);
          return;
        }
        if (t.equals("lhs")) {
          parseLinkedHashSet(out);
          return;
        }
        if (t.equals("treeset")) {
          parseTreeSet(out);
          return;
        }
        if (t.equals("ciset")) {
          parseCISet(out);
          return;
        }
        if (eqOneOf(t, "hashmap", "hm")) {
          consume();
          parseMap(new HashMap(), out);
          return;
        }
        if (t.equals("lhm")) {
          consume();
          parseMap(new LinkedHashMap(), out);
          return;
        }
        if (t.equals("tm")) {
          consume();
          parseMap(new TreeMap(), out);
          return;
        }
        if (t.equals("cimap")) {
          consume();
          parseMap(ciMap(), out);
          return;
        }
        if (t.equals("ll")) {
          consume();
          LinkedList l = new LinkedList();
          if (refID >= 0)
            refs.put(refID, l);
          {
            parseList(l, out);
            return;
          }
        }
        if (t.equals("syncLL")) {
          consume();
          {
            parseList(synchroLinkedList(), out);
            return;
          }
        }
        if (t.equals("sync")) {
          consume();
          {
            parse(new unstructure_Receiver() {

              public void set(Object value) {
                if (value instanceof Map) {
                  if (value instanceof NavigableMap) {
                    out.set(synchroNavigableMap((NavigableMap) value));
                    return;
                  }
                  if (value instanceof SortedMap) {
                    out.set(synchroSortedMap((SortedMap) value));
                    return;
                  }
                  {
                    out.set(synchroMap((Map) value));
                    return;
                  }
                } else {
                  out.set(synchroList((List) value));
                  return;
                }
              }
            });
            return;
          }
        }
        if (t.equals("ba")) {
          consume();
          String hex = trivialUnquote(tpp());
          out.set(hexToBytes(hex));
          return;
        }
        if (t.equals("boolarray")) {
          consume();
          int n = parseInt(tpp());
          String hex = trivialUnquote(tpp());
          out.set(boolArrayFromBytes(hexToBytes(hex), n));
          return;
        }
        if (t.equals("class")) {
          out.set(parseClass());
          return;
        }
        if (t.equals("l")) {
          parseLisp(out);
          return;
        }
        if (t.equals("null")) {
          consume();
          out.set(null);
          return;
        }
        if (eq(t, "c")) {
          consume();
          t = t();
          assertTrue(isJavaIdentifier(t));
          concepts.add(t);
        }
        if (eq(t, "cu")) {
          consume();
          t = tpp();
          assertTrue(isJavaIdentifier(t));
          String fullClassName = mcDollar + t;
          Class _c = findAClass(fullClassName);
          if (_c == null)
            throw fail("Class not found: " + fullClassName);
          parse(new unstructure_Receiver() {

            public void set(Object value) {
              out.set(call(_c, "_deserialize", value));
            }
          });
          return;
        }
      }
      if (eq(t, "j")) {
        consume();
        out.set(parseJava());
        return;
      }
      if (eq(t, "bc")) {
        consume();
        String c1 = tpp();
        String c2 = tpp();
        baseClassMap.put(c1, c2);
        {
          parse_inner(refID, i, out);
          return;
        }
      }
      if (c == null && !isJavaIdentifier(t))
        throw new RuntimeException("Unknown token " + (i + 1) + ": " + quote(t));
      consume();
      String className, fullClassName;
      if (eq(t(), ".")) {
        className = t;
        do {
          consume();
          className += "." + assertIdentifier(tpp());
        } while (eq(t(), "."));
        fullClassName = className;
      } else {
        className = t;
        fullClassName = mcDollar + t;
      }
      if (c == null && !allDynamic) {
        c = findAClass(fullClassName);
        handlers.put(className, c);
      }
      if (c == null && !allDynamic) {
        Set<String> seen = new HashSet();
        String parent = className;
        while (true) {
          String baseName = baseClassMap.get(parent);
          if (baseName == null)
            break;
          if (!seen.add(baseName))
            throw fail("Cyclic superclass info: " + baseName);
          c = findAClass(mcDollar + baseName);
          if (c == null)
            print("Base class " + baseName + " of " + parent + " doesn't exist either");
          else if (isAbstract(c))
            print("Can't instantiate abstract base class: " + c);
          else {
            printVars_str("Reverting to base class", "className", className, "baseName", baseName, "c", c);
            handlers.put(className, c);
            break;
          }
          parent = baseName;
        }
      }
      boolean hasBracket = eq(t(), "(");
      if (hasBracket)
        consume();
      boolean hasOuter = hasBracket && startsWith(t(), "this$");
      DynamicObject dO = null;
      Object o = null;
      final String thingName = t;
      try {
        if (c != null) {
          if (hasOuter)
            try {
              Constructor ctor = innerClassConstructors.get(c);
              if (ctor == null)
                innerClassConstructors.put(c, ctor = nuStubInnerObject_findConstructor(c, classFinder));
              o = ctor.newInstance(new Object[] { null });
            } catch (Exception e) {
              print("Error deserializing " + c + ": " + e);
              o = nuEmptyObject(c);
            }
          else
            o = nuEmptyObject(c);
          if (o instanceof DynamicObject)
            dO = (DynamicObject) o;
        } else {
          if (concepts.contains(t) && (c = findAClass(mcDollar + "Concept")) != null)
            o = dO = (DynamicObject) nuEmptyObject(c);
          else
            dO = new DynamicObject();
          dO.className = className;
        }
      } catch (Throwable __e) {
        pcallFail(__e);
      }
      if (o == null && dO == null)
        dO = new DynamicObject();
      if (refID >= 0)
        refs.put(refID, o != null ? o : dO);
      tokrefs.put(tokIndex, o != null ? o : dO);
      HashMap<String, Object> fields = new HashMap();
      Object _o = o;
      DynamicObject _dO = dO;
      if (hasBracket) {
        stack.add(new Runnable() {

          public void run() {
            try {
              if (eq(t(), ","))
                consume();
              if (eq(t(), ")")) {
                consume(")");
                objRead(_o, _dO, fields, hasOuter);
                out.set(_o != null ? _o : _dO);
              } else {
                final String key = unquote(tpp());
                String t = tpp();
                if (!eq(t, "="))
                  throw fail("= expected, got " + t + " after " + quote(key) + " in object " + thingName);
                stack.add(this);
                parse(new unstructure_Receiver() {

                  public void set(Object value) {
                    fields.put(key, value);
                  }
                });
              }
            } catch (Exception __e) {
              throw rethrow(__e);
            }
          }

          public String toString() {
            return "ifdef unstructure_debug\r\n          print(\"in object values, token: \" + t());\r...";
          }
        });
      } else {
        objRead(o, dO, fields, hasOuter);
        out.set(o != null ? o : dO);
      }
    }

    public void objRead(Object o, DynamicObject dO, Map<String, Object> fields, boolean hasOuter) {
      Object outer = fields.get("this$0");
      if (outer != null)
        fields.put("this$1", outer);
      else {
        outer = fields.get("this$1");
        if (outer != null)
          fields.put("this$0", outer);
      }
      if (o != null) {
        if (dO != null) {
          setOptAllDyn_pcall(dO, fields);
        } else {
          setOptAll_pcall(o, fields);
        }
        if (hasOuter)
          fixOuterRefs(o);
      } else
        for (Map.Entry<String, Object> e : fields.entrySet()) setDynObjectValue(dO, intern(e.getKey()), e.getValue());
      if (o != null)
        pcallOpt_noArgs(o, "_doneLoading");
    }

    public void parseSet(final Set set, final unstructure_Receiver out) {
      this.parseList(new ArrayList(), new unstructure_Receiver() {

        public void set(Object o) {
          set.addAll((List) o);
          out.set(set);
        }
      });
    }

    public void parseLisp(final unstructure_Receiver out) {
      throw fail("class Lisp not included");
    }

    public void parseBitSet(final unstructure_Receiver out) {
      consume("bitset");
      consume("{");
      final BitSet bs = new BitSet();
      stack.add(new Runnable() {

        public void run() {
          try {
            if (eq(t(), "}")) {
              consume("}");
              out.set(bs);
            } else {
              stack.add(this);
              parse(new unstructure_Receiver() {

                public void set(Object o) {
                  bs.set((Integer) o);
                  if (eq(t(), ","))
                    consume();
                }
              });
            }
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "if (eq(t(), \"}\")) {\r\n        consume(\"}\");\r\n        out.set(bs);\r\n      } els...";
        }
      });
    }

    public void parseList(final List list, final unstructure_Receiver out) {
      tokrefs.put(i, list);
      consume("[");
      stack.add(new Runnable() {

        public void run() {
          try {
            if (eq(t(), "]")) {
              consume();
              out.set(list);
            } else {
              stack.add(this);
              parse(new unstructure_Receiver() {

                public void set(Object o) {
                  list.add(o);
                  if (eq(t(), ","))
                    consume();
                }
              });
            }
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "if (eq(t(), \"]\")) {\r\n        consume();\r\n        ifdef unstructure_debug\r\n   ...";
        }
      });
    }

    public void parseArray(unstructure_Receiver out) {
      String _type = tpp();
      int dims;
      if (eq(t(), "S")) {
        _type = "S";
        consume();
      }
      if (eq(t(), "/")) {
        consume();
        dims = parseInt(tpp());
      } else
        dims = 1;
      consume("{");
      List list = new ArrayList();
      String type = _type;
      stack.add(new Runnable() {

        public void run() {
          try {
            if (eq(t(), "}")) {
              consume("}");
              if (dims > 1) {
                Class atype;
                if (type.equals("intarray"))
                  atype = int.class;
                else if (type.equals("S"))
                  atype = String.class;
                else
                  throw todo("multi-dimensional arrays of other types");
                out.set(list.toArray((Object[]) newMultiDimensionalOuterArray(atype, dims, l(list))));
              } else
                out.set(type.equals("intarray") ? toIntArray(list) : type.equals("dblarray") ? toDoubleArray(list) : type.equals("floatarray") ? toFloatArray(list) : type.equals("S") ? toStringArray(list) : list.toArray());
            } else {
              stack.add(this);
              parse(new unstructure_Receiver() {

                public void set(Object o) {
                  list.add(o);
                  if (eq(t(), ","))
                    consume();
                }
              });
            }
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "if (eq(t(), \"}\")) {\r\n        consume(\"}\");\r\n        if (dims > 1) {\r\n        ...";
        }
      });
    }

    public Object parseClass() {
      consume("class");
      consume("(");
      String name = unquote(tpp());
      consume(")");
      Class c = allDynamic ? null : findAClass(name);
      if (c != null)
        return c;
      DynamicObject dO = new DynamicObject();
      dO.className = "java.lang.Class";
      name = dropPrefix(mcDollar, name);
      dynamicObject_setRawFieldValue(dO, "name", name);
      return dO;
    }

    public Object parseBigInt() {
      consume("bigint");
      consume("(");
      String val = tpp();
      if (eq(val, "-"))
        val = "-" + tpp();
      consume(")");
      return new BigInteger(val);
    }

    public Object parseDouble() {
      consume("d");
      consume("(");
      String val = unquote(tpp());
      consume(")");
      return Double.parseDouble(val);
    }

    public Object parseFloat() {
      consume("fl");
      String val;
      if (eq(t(), "(")) {
        consume("(");
        val = unquote(tpp());
        consume(")");
      } else {
        val = unquote(tpp());
      }
      return Float.parseFloat(val);
    }

    public void parseHashSet(unstructure_Receiver out) {
      consume("hashset");
      parseSet(new HashSet(), out);
    }

    public void parseLinkedHashSet(unstructure_Receiver out) {
      consume("lhs");
      parseSet(new LinkedHashSet(), out);
    }

    public void parseTreeSet(unstructure_Receiver out) {
      consume("treeset");
      parseSet(new TreeSet(), out);
    }

    public void parseCISet(unstructure_Receiver out) {
      consume("ciset");
      parseSet(ciSet(), out);
    }

    public void parseMap(unstructure_Receiver out) {
      parseMap(new TreeMap(), out);
    }

    public Object parseJava() {
      String j = unquote(tpp());
      Matches m = new Matches();
      if (jmatch("java.awt.Color[r=*,g=*,b=*]", j, m))
        return nuObject("java.awt.Color", parseInt(m.unq(0)), parseInt(m.unq(1)), parseInt(m.unq(2)));
      else {
        warn("Unknown Java object: " + j);
        return null;
      }
    }

    public void parseMap(final Map map, final unstructure_Receiver out) {
      consume("{");
      stack.add(new Runnable() {

        public boolean v = false;

        public Object key;

        public void run() {
          if (v) {
            v = false;
            stack.add(this);
            if (!eq(tpp(), "="))
              throw fail("= expected, got " + t() + " in map of size " + l(map));
            parse(new unstructure_Receiver() {

              public void set(Object value) {
                map.put(key, value);
                if (eq(t(), ","))
                  consume();
              }
            });
          } else {
            if (eq(t(), "}")) {
              consume("}");
              out.set(map);
            } else {
              v = true;
              stack.add(this);
              parse(new unstructure_Receiver() {

                public void set(Object o) {
                  key = o;
                }
              });
            }
          }
        }
      });
    }

    public void consume() {
      curT = tok.next();
      ++i;
    }

    public void consume(String s) {
      if (!eq(t(), s)) {
        throw fail(quote(s) + " expected, got " + quote(t()));
      }
      consume();
    }

    public void parse_initial(unstructure_Receiver out) {
      consume();
      parse(out);
      while (nempty(stack)) popLast(stack).run();
    }
  }

  static public Object unstructure_tok(Producer<String> tok, boolean allDynamic, Object classFinder) {
    boolean debug = unstructure_debug;
    AutoCloseable __1 = tempSetTL(dynamicObjectIsLoading_threadLocal(), true);
    try {
      Var v = new Var();
      var unstructurer = new Unstructurer().tok(tok).allDynamic(allDynamic).classFinder(classFinder);
      unstructurer.parse_initial(new unstructure_Receiver() {

        public void set(Object o) {
          v.set(o);
        }
      });
      unstructure_tokrefs = unstructurer.tokrefs.size();
      return v.get();
    } finally {
      _close(__1);
    }
  }

  static public boolean unstructure_debug = false;

  static public Producer<String> javaTokC_noMLS_onReader(final BufferedReader r) {
    final class X implements Producer<String> {

      public StringBuilder buf = new StringBuilder();

      public char c, d, e = 'x';

      public X() {
        nc();
        nc();
        nc();
      }

      public void nc() {
        try {
          c = d;
          d = e;
          if (e == '\0')
            return;
          int i = r.read();
          e = i < 0 ? '\0' : i == '\0' ? '_' : (char) i;
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public void ncSave() {
        if (c != '\0') {
          buf.append(c);
          nc();
        }
      }

      public String next() {
        while (c != '\0') {
          if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
            nc();
          else if (c == '/' && d == '*') {
            do nc(); while (c != '\0' && !(c == '*' && d == '/'));
            nc();
            nc();
          } else if (c == '/' && d == '/') {
            do nc(); while (c != '\0' && "\r\n".indexOf(c) < 0);
          } else
            break;
        }
        if (c == '\0')
          return null;
        if (c == '\'' || c == '"') {
          char opener = c;
          ncSave();
          while (c != '\0') {
            if (c == opener || c == '\n') {
              ncSave();
              break;
            } else if (c == '\\') {
              ncSave();
              ncSave();
            } else
              ncSave();
          }
        } else if (Character.isJavaIdentifierStart(c))
          do ncSave(); while (Character.isJavaIdentifierPart(c) || c == '\'');
        else if (Character.isDigit(c)) {
          do ncSave(); while (Character.isDigit(c));
          if (c == 'L')
            ncSave();
        } else
          ncSave();
        String t = buf.toString();
        buf.setLength(0);
        return t;
      }
    }
    return new X();
  }

  static public Map<Class, Constructor> nuObjectWithoutArguments_cache = newDangerousWeakHashMap();

  static public Object nuObjectWithoutArguments(String className) {
    try {
      return nuObjectWithoutArguments(classForName(className));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public <A> A nuObjectWithoutArguments(Class<A> c) {
    try {
      if (nuObjectWithoutArguments_cache == null)
        return (A) nuObjectWithoutArguments_findConstructor(c).newInstance();
      Constructor m = nuObjectWithoutArguments_cache.get(c);
      if (m == null)
        nuObjectWithoutArguments_cache.put(c, m = nuObjectWithoutArguments_findConstructor(c));
      return (A) m.newInstance();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Constructor nuObjectWithoutArguments_findConstructor(Class c) {
    for (Constructor m : getDeclaredConstructors_cached(c)) if (empty(m.getParameterTypes())) {
      makeAccessible(m);
      return m;
    }
    throw fail("No default constructor found in " + c.getName());
  }

  static public <A> List<A> sorted(Collection<A> c, Object comparator) {
    List<A> l = cloneList(c);
    sort(l, makeComparator(comparator));
    return l;
  }

  static public <A> List<A> sorted(Collection<A> c) {
    List<A> l = cloneList(c);
    sort(l);
    return l;
  }

  static public <A> List<A> sorted(Comparator<A> comparator, Collection<A> c) {
    List<A> l = cloneList(c);
    sort(l, comparator);
    return l;
  }

  static public List<String> methodsStartingWith(Object o, final String prefix) {
    return filter(allMethodNames(o), new F1<String, Object>() {

      public Object get(String s) {
        try {
          return startsWith(s, prefix);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "startsWith(s, prefix)";
      }
    });
  }

  static volatile public PersistableThrowable _handleException_lastException;

  static public List _handleException_onException = synchroList(ll((IVF1<Throwable>) (__1 -> printStackTrace2(__1))));

  static public boolean _handleException_showThreadCancellations = false;

  static public void _handleException(Throwable e) {
    _handleException_lastException = persistableThrowable(e);
    Throwable e2 = innerException(e);
    if (e2.getClass() == RuntimeException.class && eq(e2.getMessage(), "Thread cancelled.") || e2 instanceof InterruptedException) {
      if (_handleException_showThreadCancellations)
        System.out.println(getStackTrace_noRecord(e2));
      return;
    }
    for (Object f : cloneList(_handleException_onException)) try {
      callF(f, e);
    } catch (Throwable e3) {
      try {
        printStackTrace2(e3);
      } catch (Throwable e4) {
        System.out.println(getStackTrace(e3));
        System.out.println(getStackTrace(e4));
      }
    }
  }

  static public boolean interruptThread_verbose = false;

  static public void interruptThread(Thread t) {
    if (t == null)
      return;
    if (interruptThread_verbose)
      print("Interrupting thread " + t);
    vm_threadInterruptionReasonsMap().put(t, getStackTrace());
    t.interrupt();
    URLConnection c = (URLConnection) (vm_generalSubMap("URLConnection per thread").get(t));
    if (c != null) {
      try {
        print("Closing URLConnection of interrupted thread.");
        call(c, "disconnect");
      } catch (Throwable __e) {
        pcallFail(__e);
      }
    }
  }

  static public boolean isJavaXClassLoader(ClassLoader cl) {
    return startsWithOneOf(className(cl), "main$JavaXClassLoader", "x30$JavaXClassLoader");
  }

  static public void setOptAll(Object o, Map<String, Object> fields) {
    if (fields == null)
      return;
    for (String field : keys(fields)) setOpt(o, field, fields.get(field));
  }

  static public void setOptAll(Object o, Object... values) {
    warnIfOddCount(values);
    for (int i = 0; i + 1 < l(values); i += 2) {
      String field = (String) values[i];
      Object value = values[i + 1];
      setOpt(o, field, value);
    }
  }

  static public Concepts newConceptsWithClassFinder(String progID) {
    Concepts cc = new Concepts(progID);
    cc.classFinder = _defaultClassFinder();
    return cc;
  }

  static public Concepts newConceptsWithClassFinder(File conceptsFile) {
    Concepts cc = new Concepts(assertNotNull(conceptsFile));
    cc.classFinder = _defaultClassFinder();
    return cc;
  }

  static public Concepts newConceptsWithClassFinder(File conceptsFile, IF1<String, Class> classFinder) {
    Concepts cc = new Concepts(assertNotNull(conceptsFile));
    cc.classFinder = classFinder;
    return cc;
  }

  static public String getDBProgramID_id;

  static public String getDBProgramID() {
    return nempty(getDBProgramID_id) ? getDBProgramID_id : programIDWithCase();
  }

  static public <A, B> Map<A, B> weakHashMap() {
    return newWeakHashMap();
  }

  static public int hashCode(Object a) {
    return a == null ? 0 : a.hashCode();
  }

  static public int hashCode(long l) {
    return Long.hashCode(l);
  }

  static public int hashCode(double d) {
    return Double.hashCode(d);
  }

  static public Map vm_generalWeakSubMap(Object name) {
    synchronized (vm_generalMap()) {
      Map map = (Map) (vm_generalMap_get(name));
      if (map == null)
        vm_generalMap_put(name, map = newWeakMap());
      return map;
    }
  }

  static public <A> WeakReference<A> weakRef(A a) {
    return newWeakReference(a);
  }

  static public <A> A getNext(Iterator<A> it) {
    return nextFromIterator(it);
  }

  static public <A> IterableIterator<A> iteratorFromFunction_withEndMarker_f0(final F0<A> f) {
    class IFF2 extends IterableIterator<A> {

      public A a;

      public boolean have, done;

      public boolean hasNext() {
        getNext();
        return !done;
      }

      public A next() {
        getNext();
        if (done)
          throw fail();
        A _a = a;
        a = null;
        have = false;
        return _a;
      }

      public void getNext() {
        if (done || have)
          return;
        Object o = f.get();
        if (o == iteratorFromFunction_endMarker) {
          done = true;
          return;
        }
        a = (A) o;
        have = true;
      }
    }
    ;
    return new IFF2();
  }

  static public boolean isTrueOpt(Object o) {
    if (o instanceof Boolean)
      return ((Boolean) o).booleanValue();
    return false;
  }

  static public boolean isTrueOpt(String field, Object o) {
    return isTrueOpt(getOpt(field, o));
  }

  static public boolean eqicOneOf(String s, String... l) {
    for (String x : l) if (eqic(s, x))
      return true;
    return false;
  }

  static public List<String> isYes_yesses = litlist("y", "yes", "yeah", "y", "yup", "yo", "corect", "sure", "ok", "afirmative");

  static public boolean isYes(String s) {
    return isYes_yesses.contains(collapseWord(toLowerCase(firstWord2(s))));
  }

  static public TimeZone localTimeZone() {
    return getTimeZone(standardTimeZone());
  }

  static public Comparator<String> caseInsensitiveComparator() {
    return betterCIComparator();
  }

  static public TreeSet<String> toCaseInsensitiveSet_treeSet(Iterable<String> c) {
    if (isCISet(c))
      return (TreeSet) c;
    TreeSet<String> set = caseInsensitiveSet_treeSet();
    addAll(set, c);
    return set;
  }

  static public TreeSet<String> toCaseInsensitiveSet_treeSet(String... x) {
    TreeSet<String> set = caseInsensitiveSet_treeSet();
    addAll(set, x);
    return set;
  }

  static public Object interceptPrintInThisThread(Object f) {
    Object old = print_byThread().get();
    print_byThread().set(f);
    return old;
  }

  static public int rfindCodeTokens(List<String> tok, String... tokens) {
    return rfindCodeTokens(tok, 1, false, tokens);
  }

  static public int rfindCodeTokens(List<String> tok, boolean ignoreCase, String... tokens) {
    return rfindCodeTokens(tok, 1, ignoreCase, tokens);
  }

  static public int rfindCodeTokens(List<String> tok, int startIdx, boolean ignoreCase, String... tokens) {
    return rfindCodeTokens(tok, startIdx, ignoreCase, tokens, null);
  }

  static public List<String> rfindCodeTokens_specials = litlist("*", "<quoted>", "<id>", "<int>", "\\*");

  static public boolean rfindCodeTokens_debug = false;

  static public int rfindCodeTokens_indexed, rfindCodeTokens_unindexed;

  static public int rfindCodeTokens_bails, rfindCodeTokens_nonbails;

  static public int rfindCodeTokens(List<String> tok, int startIdx, boolean ignoreCase, String[] tokens, Object condition) {
    return rfindCodeTokens(tok, startIdx, l(tok), ignoreCase, tokens, condition);
  }

  static public int rfindCodeTokens(List<String> tok, int startIdx, int endIndex, boolean ignoreCase, String[] tokens, Object condition) {
    if (rfindCodeTokens_debug) {
      if (eq(getClassName(tok), "main$IndexedList2"))
        rfindCodeTokens_indexed++;
      else
        rfindCodeTokens_unindexed++;
    }
    if (!rfindCodeTokens_specials.contains(tokens[0]) && !tok.contains(tokens[0])) {
      ++rfindCodeTokens_bails;
      return -1;
    }
    ++rfindCodeTokens_nonbails;
    outer: for (int i = min(endIndex, tok.size() - tokens.length * 2) | 1; i >= startIdx; i -= 2) {
      for (int j = 0; j < tokens.length; j++) {
        String p = tokens[j], t = tok.get(i + j * 2);
        boolean match = false;
        if (eq(p, "*"))
          match = true;
        else if (eq(p, "<quoted>"))
          match = isQuoted(t);
        else if (eq(p, "<id>"))
          match = isIdentifier(t);
        else if (eq(p, "<int>"))
          match = isInteger(t);
        else if (eq(p, "\\*"))
          match = eq("*", t);
        else
          match = ignoreCase ? eqic(p, t) : eq(p, t);
        if (!match)
          continue outer;
      }
      if (condition == null || checkTokCondition(condition, tok, i - 1))
        return i;
    }
    return -1;
  }

  static public void clearTokens(List<String> tok) {
    clearAllTokens(tok);
  }

  static public void clearTokens(List<String> tok, int i, int j) {
    clearAllTokens(tok, i, j);
  }

  static public void clearTokens(List<String> tok, IntRange r) {
    clearAllTokens(tok, r.start, r.end);
  }

  static public List<String> allToUpper(Collection<String> l) {
    List<String> x = new ArrayList(l(l));
    if (l != null)
      for (String s : l) x.add(upper(s));
    return x;
  }

  static public <A> int indexOfSubList(List<A> x, List<A> y) {
    return indexOfSubList(x, y, 0);
  }

  static public <A> int indexOfSubList(List<A> x, List<A> y, int i) {
    outer: for (; i + l(y) <= l(x); i++) {
      for (int j = 0; j < l(y); j++) if (neq(x.get(i + j), y.get(j)))
        continue outer;
      return i;
    }
    return -1;
  }

  static public <A> int indexOfSubList(List<A> x, A[] y, int i) {
    outer: for (; i + l(y) <= l(x); i++) {
      for (int j = 0; j < l(y); j++) if (neq(x.get(i + j), y[j]))
        continue outer;
      return i;
    }
    return -1;
  }

  static public <A, B extends A> void copyListPart(List<B> a, int i1, List<A> b, int i2, int n) {
    if (a == null || b == null)
      return;
    for (int i = 0; i < n; i++) b.set(i2 + i, a.get(i1 + i));
  }

  static public boolean eqGet(List l, int i, Object o) {
    return eq(get(l, i), o);
  }

  static public <A, B> boolean eqGet(Map<A, B> map, A key, Object o) {
    return eq(mapGet(map, key), o);
  }

  static public int findEndOfTypeArgs(List<String> cnc, int i) {
    int j = i + 2, level = 1;
    while (j < cnc.size()) {
      if (cnc.get(j).equals("<"))
        ++level;
      else if (cnc.get(j).equals(">"))
        --level;
      if (level == 0)
        return j + 1;
      ++j;
    }
    return cnc.size();
  }

  static public int findEndOfBracketPart2(List<String> cnc, int i) {
    int j = i + 2, level = 1;
    while (j < cnc.size()) {
      if (eqOneOf(cnc.get(j), "{", "(", "["))
        ++level;
      else if (eqOneOf(cnc.get(j), "}", ")", "]"))
        --level;
      if (level == 0)
        return j + 1;
      ++j;
    }
    return cnc.size();
  }

  static public boolean checkCondition(Object condition, Object... args) {
    return isTrue(callF(condition, args));
  }

  static public <A> boolean checkCondition(IF1<A, Boolean> condition, A arg) {
    return isTrue(callF(condition, arg));
  }

  static public String htmlencode2(String s) {
    return htmlencode_noQuotes(s);
  }

  static public boolean odd(int i) {
    return (i & 1) != 0;
  }

  static public boolean odd(long i) {
    return (i & 1) != 0;
  }

  static public boolean odd(BigInteger i) {
    return odd(toInt(i));
  }

  static public <A> List<A> newSubListOrSame(List<A> l, int startIndex) {
    return newSubListOrSame(l, startIndex, l(l));
  }

  static public <A> List<A> newSubListOrSame(List<A> l, int startIndex, int endIndex) {
    if (l == null)
      return null;
    int n = l(l);
    startIndex = max(0, startIndex);
    endIndex = min(n, endIndex);
    if (startIndex >= endIndex)
      return ll();
    if (startIndex == 0 && endIndex == n)
      return l;
    return cloneList(l.subList(startIndex, endIndex));
  }

  static public <A> List<A> newSubListOrSame(List<A> l, IntRange r) {
    return newSubListOrSame(l, r.start, r.end);
  }

  static public int[] takeFirstOfIntArray(int[] b, int n) {
    return subIntArray(b, 0, n);
  }

  static public int[] takeFirstOfIntArray(int n, int[] b) {
    return takeFirstOfIntArray(b, n);
  }

  static public short[] takeFirstOfShortArray(short[] b, int n) {
    return subShortArray(b, 0, n);
  }

  static public short[] takeFirstOfShortArray(int n, short[] b) {
    return takeFirstOfShortArray(b, n);
  }

  static public byte[] takeFirstOfByteArray(byte[] b, int n) {
    return subByteArray(b, 0, n);
  }

  static public byte[] takeFirstOfByteArray(int n, byte[] b) {
    return takeFirstOfByteArray(b, n);
  }

  static public double[] takeFirstOfDoubleArray(double[] b, int n) {
    return subDoubleArray(b, 0, n);
  }

  static public double[] takeFirstOfDoubleArray(int n, double[] b) {
    return takeFirstOfDoubleArray(b, n);
  }

  static public <A, B> Map<A, B> takeFirstFromMap(int n, Map<A, B> map) {
    if (map == null)
      return null;
    Map<A, B> map2 = similarEmptyMap(map);
    if (n > 0)
      for (Map.Entry<A, B> e : map.entrySet()) if (n-- <= 0)
        break;
      else
        map2.put(e.getKey(), e.getValue());
    return map2;
  }

  static public Class run(String progID, String... args) {
    Class main = hotwire(progID);
    callMain(main, args);
    return main;
  }

  static public void restart() {
    Object j = getJavaX();
    call(j, "cleanRestart", get(j, "fullArgs"));
  }

  static public Object[] mapToParams(Map map) {
    return mapToObjectArray(map);
  }

  static public Object[] unrollParams(Object[] params) {
    if (l(params) == 1 && params[0] instanceof Map)
      return mapToParams((Map) params[0]);
    return params;
  }

  static public Object html_valueLessParam_cache;

  static public Object html_valueLessParam() {
    if (html_valueLessParam_cache == null)
      html_valueLessParam_cache = html_valueLessParam_load();
    return html_valueLessParam_cache;
  }

  static public Object html_valueLessParam_load() {
    return new Object();
  }

  static public String htmlQuote(String s) {
    return "\"" + htmlencode_forParams(s) + "\"";
  }

  static public TreeSet<String> litciset(String... items) {
    TreeSet<String> set = caseInsensitiveSet();
    for (String a : items) set.add(a);
    return set;
  }

  static public TreeSet<Symbol> litciset(Symbol... items) {
    TreeSet<Symbol> set = treeSet();
    for (Symbol a : items) set.add(a);
    return set;
  }

  static public String dropSuffixIgnoreCase(String suffix, String s) {
    return ewic(s, suffix) ? s.substring(0, l(s) - l(suffix)) : s;
  }

  static public boolean ewicOneOf(String s, String... l) {
    if (s != null)
      for (String x : l) if (ewic(s, x))
        return true;
    return false;
  }

  static public int typeConversionScore(Class a, Class b) {
    if (a == b)
      return 0;
    if (b.isPrimitive()) {
      if (a.isPrimitive()) {
        if (b == boolean.class)
          return Integer.MAX_VALUE;
        if (b == byte.class)
          return Integer.MAX_VALUE;
        if (b == char.class)
          return a == byte.class ? -2 : Integer.MAX_VALUE;
        if (b == short.class)
          return a == byte.class ? -2 : Integer.MAX_VALUE;
        if (b == int.class)
          return a == byte.class || a == char.class || a == short.class ? -2 : Integer.MAX_VALUE;
        if (b == long.class)
          return a == byte.class || a == char.class || a == short.class || a == int.class ? -2 : Integer.MAX_VALUE;
        if (b == float.class)
          return a == byte.class || a == char.class || a == short.class || a == int.class ? -2 : Integer.MAX_VALUE;
        return a != boolean.class ? -2 : Integer.MAX_VALUE;
      } else {
        return primitiveToBoxedType(b) == a ? 1 : Integer.MAX_VALUE;
      }
    } else {
      if (a.isPrimitive())
        return primitiveToBoxedType(a) == b ? 1 : Integer.MAX_VALUE;
      if (!b.isAssignableFrom(a))
        return Integer.MAX_VALUE;
      if (a.isInterface() || b.isInterface())
        return 1;
      if (a.isArray() && b.isArray())
        return typeConversionScore(a.getComponentType(), b.getComponentType()) / 2;
      return subclassDistance(a, b) * 2;
    }
  }

  static public Class boxedToPrimitiveType(Class type) {
    if (type == Boolean.class)
      return boolean.class;
    if (type == Integer.class)
      return int.class;
    if (type == Long.class)
      return long.class;
    if (type == Float.class)
      return float.class;
    if (type == Short.class)
      return short.class;
    if (type == Character.class)
      return char.class;
    if (type == Byte.class)
      return byte.class;
    if (type == Double.class)
      return double.class;
    return null;
  }

  static public <A> A liftLast(List<A> l) {
    if (empty(l))
      return null;
    int i = l(l) - 1;
    A a = l.get(i);
    l.remove(i);
    return a;
  }

  static public <A> List<A> liftLast(int n, List<A> l) {
    int i = l(l) - n;
    List<A> part = cloneSubList(l, i);
    removeSubList(l, i);
    return part;
  }

  static public String stringIfTrue(boolean b, String s) {
    return b ? s : "";
  }

  static public Class actualMC() {
    return or((Class) realMC(), mc());
  }

  static public boolean isSubclass(Class a, Class b) {
    return a != null && b != null && b.isAssignableFrom(a);
  }

  static public boolean isBoxedType(Class type) {
    return type == Boolean.class || type == Integer.class || type == Long.class || type == Float.class || type == Short.class || type == Character.class || type == Byte.class || type == Double.class;
  }

  static public boolean hasThisDollarFields(Object o) {
    Matches m = new Matches();
    for (var f : allFieldObjects_dontMakeAccessible(o)) if (startsWith(f.getName(), "this$", m) && isInteger(m.rest()))
      return true;
    return false;
  }

  static public boolean hasSingleArgumentConstructor(Class c) {
    if (c != null)
      for (Constructor m : getDeclaredConstructors_cached(c)) if (l(m.getParameterTypes()) == 1)
        return true;
    return false;
  }

  static public byte[] boolArrayToBytes(boolean[] a) {
    byte[] b = new byte[(l(a) + 7) / 8];
    for (int i = 0; i < l(a); i++) if (a[i])
      b[i / 8] |= 1 << (i & 7);
    return b;
  }

  static public byte[] byteArrayFromShorts_bigEndian(short[] a) {
    return byteArrayFromShorts_bigEndian(a, 0, l(a));
  }

  static public byte[] byteArrayFromShorts_bigEndian(short[] a, int from, int to) {
    byte[] b = new byte[(to - from) * 2];
    for (int i = 0; i < a.length; i++) {
      short s = a[from + i];
      b[i * 2] = (byte) (s >> 8);
      b[i * 2 + 1] = (byte) s;
    }
    return b;
  }

  static public byte[] byteArrayFromLongs_bigEndian(long[] a) {
    return byteArrayFromLongs_bigEndian(a, 0, l(a));
  }

  static public byte[] byteArrayFromLongs_bigEndian(long[] a, int from, int to) {
    byte[] b = new byte[(to - from) * 8];
    for (int i = 0; i < a.length; i++) {
      long s = a[from + i];
      b[i * 8] = (byte) (s >> 56);
      b[i * 8 + 1] = (byte) (s >> 48);
      b[i * 8 + 2] = (byte) (s >> 40);
      b[i * 8 + 3] = (byte) (s >> 32);
      b[i * 8 + 4] = (byte) (s >> 24);
      b[i * 8 + 5] = (byte) (s >> 16);
      b[i * 8 + 6] = (byte) (s >> 8);
      b[i * 8 + 7] = (byte) s;
    }
    return b;
  }

  static public <A, B, C> List<Pair<A, C>> mapPairB(final Object f, Iterable<Pair<A, B>> l) {
    return map(l, new F1<Pair<A, B>, Pair<A, C>>() {

      public Pair<A, C> get(Pair<A, B> p) {
        try {
          return p == null ? null : pair(p.a, (C) callF(f, p.b));
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "p == null ? null : pair(p.a, (C) callF(f, p.b))";
      }
    });
  }

  static public <A, B, C> List<Pair<A, C>> mapPairB(final F1<B, C> f, Iterable<Pair<A, B>> l) {
    return mapPairB((Object) f, l);
  }

  static public <A, B, C> List<Pair<A, C>> mapPairB(final IF1<B, C> f, Iterable<Pair<A, B>> l) {
    return mapPairB((Object) f, l);
  }

  static public <A, B, C> List<Pair<A, C>> mapPairB(Iterable<Pair<A, B>> l, IF1<B, C> f) {
    return mapPairB((Object) f, l);
  }

  static public <A, B, C> Pair<A, C> mapPairB(IF1<B, C> f, Pair<A, B> p) {
    return pairMapB(f, p);
  }

  static public <A, B, C> Pair<A, C> mapPairB(Pair<A, B> p, IF1<B, C> f) {
    return pairMapB(f, p);
  }

  static public <A, B> Map<B, A> mapToKey(Iterable<A> l, IF1<A, B> f) {
    return mapToKeys(l, f);
  }

  static public <A, B> Map<B, A> mapToKey(IF1<A, B> f, Iterable<A> l) {
    return mapToKeys(f, l);
  }

  static public Map<Class, List<String>> getFieldOrder_cache = weakMap();

  static public List<String> getFieldOrder(Object o) {
    return getFieldOrder(_getClass(o));
  }

  static public List<String> getFieldOrder(Class c) {
    if (c == null)
      return null;
    return getOrCreate(getFieldOrder_cache, c, () -> splitAtSpace(toStringOpt(getOpt(c, "_fieldOrder"))));
  }

  static public boolean zipFileContains(File inZip, String fileName) {
    try {
      ZipFile zip = new ZipFile(inZip);
      try {
        return zipFileContains(zip, fileName);
      } finally {
        zip.close();
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public boolean zipFileContains(ZipFile zip, String fileName) {
    try {
      return zip.getEntry(fileName) != null;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public File envJavaHome() {
    return newFile(System.getProperty("java.home"));
  }

  static public <A> A getWeakRef(Reference<A> ref) {
    return ref == null ? null : ref.get();
  }

  static public x30_pkg.x30_util.BetterThreadLocal<WeakReference> dm_current_generic_tl;

  static public x30_pkg.x30_util.BetterThreadLocal<WeakReference> dm_current_generic_tl() {
    if (dm_current_generic_tl == null)
      dm_current_generic_tl = vm_generalMap_getOrCreate("currentModule", () -> new x30_pkg.x30_util.BetterThreadLocal());
    return dm_current_generic_tl;
  }

  static public String programID;

  static public String getProgramID() {
    return nempty(programID) ? formatSnippetIDOpt(programID) : "?";
  }

  static public String getProgramID(Class c) {
    String id = (String) getOpt(c, "programID");
    if (nempty(id))
      return formatSnippetID(id);
    return "?";
  }

  static public String getProgramID(Object o) {
    return getProgramID(getMainClass(o));
  }

  static public void dynamicObject_setRawFieldValue(DynamicObject o, Object key, Object value) {
    if (o == null)
      return;
    synchronized (o) {
      o.fieldValues = syncMapPut2_createLinkedHashMap((LinkedHashMap) o.fieldValues, key, value);
    }
  }

  static public CloseableIterableIterator<String> linesFromFile(File f) {
    return linesFromFile(f, null);
  }

  static public CloseableIterableIterator<String> linesFromFile(File f, IResourceHolder resourceHolder) {
    try {
      if (!fileExists(f))
        return emptyCloseableIterableIterator();
      if (ewic(f.getName(), ".gz"))
        return linesFromReader(utf8bufferedReader(newGZIPInputStream(f)), resourceHolder);
      return linesFromReader(utf8bufferedReader(f), resourceHolder);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public CloseableIterableIterator<String> linesFromFile(String path) {
    return linesFromFile(path, null);
  }

  static public CloseableIterableIterator<String> linesFromFile(String path, IResourceHolder resourceHolder) {
    return linesFromFile(newFile(path), resourceHolder);
  }

  static public <A> ArrayList<A> asList_closeIterator(CloseableIterableIterator<A> it) {
    AutoCloseable __1 = it;
    try {
      return asList(it);
    } finally {
      _close(__1);
    }
  }

  static public boolean sameSnippetID(String a, String b) {
    if (!isSnippetID(a) || !isSnippetID(b))
      return false;
    return parseSnippetID(a) == parseSnippetID(b);
  }

  static public File javaxDataDir_dir;

  static public File javaxDataDir() {
    return javaxDataDir_dir != null ? javaxDataDir_dir : new File(userHome(), "JavaX-Data");
  }

  static public File javaxDataDir(String... subs) {
    return newFile(javaxDataDir(), subs);
  }

  static public String formatSnippetIDOpt(String s) {
    return isSnippetID(s) ? formatSnippetID(s) : s;
  }

  static volatile public String caseID_caseID;

  static public String caseID() {
    return caseID_caseID;
  }

  static public void caseID(String id) {
    caseID_caseID = id;
  }

  static public ThreadLocal<VF1<File>> checkFileNotTooBigToRead_tl = new ThreadLocal();

  static public void checkFileNotTooBigToRead(File f) {
    callF(checkFileNotTooBigToRead_tl.get(), f);
  }

  static public int gzInputStream_defaultBufferSize = 65536;

  static public GZIPInputStream gzInputStream(File f) {
    try {
      return gzInputStream(new FileInputStream(f));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public GZIPInputStream gzInputStream(File f, int bufferSize) {
    try {
      return gzInputStream(new FileInputStream(f), bufferSize);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public GZIPInputStream gzInputStream(InputStream in) {
    return gzInputStream(in, gzInputStream_defaultBufferSize);
  }

  static public GZIPInputStream gzInputStream(InputStream in, int bufferSize) {
    try {
      return _registerIOWrap(new GZIPInputStream(in, gzInputStream_defaultBufferSize), in);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Producer<String> javaTokC_noMLS_iterator(final String s) {
    return javaTokC_noMLS_iterator(s, 0);
  }

  static public Producer<String> javaTokC_noMLS_iterator(final String s, final int startIndex) {
    return new Producer<String>() {

      final public int l = s.length();

      public int i = startIndex;

      public String next() {
        if (i >= l)
          return null;
        int j = i;
        char c, d;
        while (j < l) {
          c = s.charAt(j);
          d = j + 1 >= l ? '\0' : s.charAt(j + 1);
          if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
            ++j;
          else if (c == '/' && d == '*') {
            do ++j; while (j < l && !s.substring(j, Math.min(j + 2, l)).equals("*/"));
            j = Math.min(j + 2, l);
          } else if (c == '/' && d == '/') {
            do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0);
          } else
            break;
        }
        i = j;
        if (i >= l)
          return null;
        c = s.charAt(i);
        d = i + 1 >= l ? '\0' : s.charAt(i + 1);
        if (c == '\'' || c == '"') {
          char opener = c;
          ++j;
          while (j < l) {
            if (s.charAt(j) == opener || s.charAt(j) == '\n') {
              ++j;
              break;
            } else if (s.charAt(j) == '\\' && j + 1 < l)
              j += 2;
            else
              ++j;
          }
        } else if (Character.isJavaIdentifierStart(c))
          do ++j; while (j < l && Character.isJavaIdentifierPart(s.charAt(j)));
        else if (Character.isDigit(c)) {
          do ++j; while (j < l && Character.isDigit(s.charAt(j)));
          if (j < l && s.charAt(j) == 'L')
            ++j;
        } else
          ++j;
        String t = quickSubstring(s, i, j);
        i = j;
        return t;
      }
    };
  }

  static public Object _defaultClassFinder_value = defaultDefaultClassFinder();

  static public Object _defaultClassFinder() {
    return _defaultClassFinder_value;
  }

  static public BigInteger parseBigInt(String s) {
    return new BigInteger(s);
  }

  static public String trivialUnquote(String s) {
    return dropPrefixSuffix("\"", s);
  }

  static public short[] shortArrayFromBytes(byte[] a) {
    return shortArrayFromBytes(a, 0, l(a));
  }

  static public short[] shortArrayFromBytes(byte[] a, int from, int to) {
    short[] b = new short[(to - from) / 2];
    for (int i = 0; i < b.length; i++) b[i] = shortFromBytes(a, from + i * 2);
    return b;
  }

  static public byte[] hexToBytes(String s) {
    if (odd(l(s)))
      throw fail("Hex string has odd length: " + quote(shorten(10, s)));
    int n = l(s) / 2;
    byte[] bytes = new byte[n];
    for (int i = 0; i < n; i++) {
      int a = parseHexChar(s.charAt(i * 2));
      int b = parseHexChar(s.charAt(i * 2 + 1));
      if (a < 0 || b < 0)
        throw fail("Bad hex byte: " + quote(substring(s, i * 2, i * 2 + 2)) + " at " + i * 2 + "/" + l(s));
      bytes[i] = (byte) ((a << 4) | b);
    }
    return bytes;
  }

  static public long[] longArrayFromBytes(byte[] a) {
    return longArrayFromBytes(a, 0, l(a));
  }

  static public long[] longArrayFromBytes(byte[] a, int from, int to) {
    long[] b = new long[(to - from) / 8];
    for (int i = 0; i < b.length; i++) b[i] = longFromBytes_bigEndian(a, from + i * 8);
    return b;
  }

  static public HashMap<String, Class> findClass_fullName_cache = new HashMap();

  static public Class findClass_fullName(String name) {
    synchronized (findClass_fullName_cache) {
      if (findClass_fullName_cache.containsKey(name))
        return findClass_fullName_cache.get(name);
      Class c;
      try {
        c = Class.forName(name);
      } catch (ClassNotFoundException e) {
        c = null;
      }
      findClass_fullName_cache.put(name, c);
      return c;
    }
  }

  static public String unquoteUsingCharArray(String s, char[] buf) {
    if (s == null)
      return null;
    if (startsWith(s, '[')) {
      int i = 1;
      while (i < s.length() && s.charAt(i) == '=') ++i;
      if (i < s.length() && s.charAt(i) == '[') {
        String m = s.substring(1, i);
        if (s.endsWith("]" + m + "]"))
          return s.substring(i + 1, s.length() - i - 1);
      }
    }
    if (s.length() > 1) {
      char c = s.charAt(0);
      if (c == '\"' || c == '\'') {
        int l = endsWith(s, c) ? s.length() - 1 : s.length();
        if (l > buf.length)
          return unquote(s);
        int n = 0;
        for (int i = 1; i < l; i++) {
          char ch = s.charAt(i);
          if (ch == '\\') {
            char nextChar = (i == l - 1) ? '\\' : s.charAt(i + 1);
            if (nextChar >= '0' && nextChar <= '7') {
              String code = "" + nextChar;
              i++;
              if ((i < l - 1) && s.charAt(i + 1) >= '0' && s.charAt(i + 1) <= '7') {
                code += s.charAt(i + 1);
                i++;
                if ((i < l - 1) && s.charAt(i + 1) >= '0' && s.charAt(i + 1) <= '7') {
                  code += s.charAt(i + 1);
                  i++;
                }
              }
              buf[n++] = (char) Integer.parseInt(code, 8);
              continue;
            }
            switch(nextChar) {
              case '\"':
                ch = '\"';
                break;
              case '\\':
                ch = '\\';
                break;
              case 'b':
                ch = '\b';
                break;
              case 'f':
                ch = '\f';
                break;
              case 'n':
                ch = '\n';
                break;
              case 'r':
                ch = '\r';
                break;
              case 't':
                ch = '\t';
                break;
              case '\'':
                ch = '\'';
                break;
              case 'u':
                if (i >= l - 5) {
                  ch = 'u';
                  break;
                }
                int code = Integer.parseInt("" + s.charAt(i + 2) + s.charAt(i + 3) + s.charAt(i + 4) + s.charAt(i + 5), 16);
                char[] x = Character.toChars(code);
                int lx = x.length;
                for (int j = 0; j < lx; j++) buf[n++] = x[j];
                i += 5;
                continue;
              default:
                ch = nextChar;
            }
            i++;
          }
          buf[n++] = ch;
        }
        return new String(buf, 0, n);
      }
    }
    return s;
  }

  static public boolean structure_isMarker(String s, int i, int j) {
    if (i >= j)
      return false;
    if (s.charAt(i) != 'm')
      return false;
    ++i;
    while (i < j) {
      char c = s.charAt(i);
      if (c < '0' || c > '9')
        return false;
      ++i;
    }
    return true;
  }

  static public String internIfLongerThan(String s, int l) {
    return s == null ? null : l(s) >= l ? intern(s) : s;
  }

  static public char unquoteCharacter(String s) {
    assertTrue(s.startsWith("'") && s.length() > 1);
    return unquote("\"" + s.substring(1, s.endsWith("'") ? s.length() - 1 : s.length()) + "\"").charAt(0);
  }

  static public boolean isLongConstant(String s) {
    if (!s.endsWith("L"))
      return false;
    s = s.substring(0, l(s) - 1);
    return isInteger(s);
  }

  static public <A> TreeMap<String, A> ciMap() {
    return caseInsensitiveMap();
  }

  static public List parseList(String s) {
    return (List) safeUnstructure(s);
  }

  static public <A> List<A> synchroLinkedList() {
    return synchroList(new LinkedList<A>());
  }

  static public <A, B> NavigableMap<A, B> synchroNavigableMap(NavigableMap<A, B> map) {
    return new SynchronizedNavigableMap(map);
  }

  static public <A, B> SortedMap<A, B> synchroSortedMap(SortedMap<A, B> map) {
    return new SynchronizedSortedMap(map);
  }

  static public boolean[] boolArrayFromBytes(byte[] a, int n) {
    boolean[] b = new boolean[n];
    int m = min(n, l(a) * 8);
    for (int i = 0; i < m; i++) b[i] = (a[i / 8] & 1 << (i & 7)) != 0;
    return b;
  }

  static public <A> Constructor nuStubInnerObject_findConstructor(Class<A> c) {
    return nuStubInnerObject_findConstructor(c, null);
  }

  static public <A> Constructor nuStubInnerObject_findConstructor(Class<A> c, Object classFinder) {
    try {
      Class outerType = getOuterClass(c, classFinder);
      Constructor m = c.getDeclaredConstructor(outerType);
      makeAccessible(m);
      return m;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Map<Class, Constructor> nuEmptyObject_cache = newDangerousWeakHashMap();

  static public <A> A nuEmptyObject(Class<A> c) {
    try {
      Constructor ctr;
      synchronized (nuEmptyObject_cache) {
        ctr = nuEmptyObject_cache.get(c);
        if (ctr == null) {
          nuEmptyObject_cache.put(c, ctr = nuEmptyObject_findConstructor(c));
          makeAccessible(ctr);
        }
      }
      try {
        return (A) ctr.newInstance();
      } catch (InstantiationException e) {
        if (empty(e.getMessage()))
          if ((c.getModifiers() & Modifier.ABSTRACT) != 0)
            throw fail("Can't instantiate abstract class " + className(c), e);
          else
            throw fail("Can't instantiate " + className(c), e);
        else
          throw rethrow(e);
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Constructor nuEmptyObject_findConstructor(Class c) {
    for (Constructor m : getDeclaredConstructors_cached(c)) if (m.getParameterTypes().length == 0)
      return m;
    throw fail("No default constructor declared in " + c.getName());
  }

  static public void setOptAllDyn_pcall(DynamicObject o, Map<String, Object> fields) {
    if (fields == null || o == null)
      return;
    HashMap<String, Field> fieldMap = instanceFieldsMap(o);
    for (Map.Entry<String, Object> e : fields.entrySet()) {
      try {
        String field = e.getKey();
        Object val = e.getValue();
        Field f = fieldMap.get(field);
        if (f != null)
          smartSet(f, o, val);
        else {
          dynamicObject_setRawFieldValue(o, intern(field), val);
        }
      } catch (Throwable __e) {
        pcallFail(__e);
      }
    }
  }

  static public void setOptAll_pcall(Object o, Map<String, Object> fields) {
    if (fields == null)
      return;
    for (String field : keys(fields)) try {
      setOpt(o, field, fields.get(field));
    } catch (Throwable __e) {
      print(exceptionToStringShort(__e));
    }
  }

  static public void setOptAll_pcall(Object o, Object... values) {
    warnIfOddCount(values);
    for (int i = 0; i + 1 < l(values); i += 2) {
      String field = (String) values[i];
      Object value = values[i + 1];
      try {
        setOpt(o, field, value);
      } catch (Throwable __e) {
        print(exceptionToStringShort(__e));
      }
    }
  }

  static public void fixOuterRefs(Object o) {
    try {
      if (o == null)
        return;
      Field[] l = thisDollarOneFields(o.getClass());
      if (l.length <= 1)
        return;
      Object father = null;
      for (Field f : l) {
        father = f.get(o);
        if (father != null)
          break;
      }
      if (father == null)
        return;
      for (Field f : l) f.set(o, father);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public void pcallOpt_noArgs(Object o, String method) {
    try {
      callOpt_noArgs(o, method);
    } catch (Throwable __e) {
      pcallFail(__e);
    }
  }

  static public RuntimeException todo() {
    throw new RuntimeException("TODO");
  }

  static public RuntimeException todo(Object msg) {
    throw new RuntimeException("TODO: " + msg);
  }

  static public Object newMultiDimensionalOuterArray(Class elementType, int dimensions, int length) {
    int[] dims = new int[dimensions];
    dims[0] = length;
    return Array.newInstance(elementType, dims);
  }

  static public int[] toIntArray(Collection<Integer> l) {
    int[] a = new int[l(l)];
    int i = 0;
    if (a.length != 0)
      for (int x : l) a[i++] = x;
    return a;
  }

  static public double[] toDoubleArray(Collection<Double> l) {
    double[] a = new double[l(l)];
    int i = 0;
    if (a.length != 0)
      for (double x : l) a[i++] = x;
    return a;
  }

  static public double[] toDoubleArray(float... l) {
    double[] a = new double[l(l)];
    for (int i = 0; i < a.length; i++) a[i] = l[i];
    return a;
  }

  static public double[] toDoubleArray(int... l) {
    double[] a = new double[l(l)];
    for (int i = 0; i < a.length; i++) a[i] = l[i];
    return a;
  }

  static public float[] toFloatArray(List<Float> l) {
    float[] a = new float[l(l)];
    for (int i = 0; i < a.length; i++) a[i] = l.get(i);
    return a;
  }

  static public float[] toFloatArray(double[] l) {
    float[] a = new float[l(l)];
    for (int i = 0; i < a.length; i++) a[i] = (float) l[i];
    return a;
  }

  static public boolean jmatch(String pat, String s) {
    return jmatch(pat, s, null);
  }

  static public boolean jmatch(String pat, String s, Matches matches) {
    if (s == null)
      return false;
    return jmatch(pat, javaTok(s), matches);
  }

  static public boolean jmatch(String pat, List<String> toks) {
    return jmatch(pat, toks, null);
  }

  static public boolean jmatch(String pat, List<String> toks, Matches matches) {
    List<String> tokpat = javaTok(pat);
    String[] m = match2(tokpat, toks);
    if (m == null)
      return false;
    else {
      if (matches != null)
        matches.m = m;
      return true;
    }
  }

  static public <A> AutoCloseable tempSetTL(ThreadLocal<A> tl, A a) {
    return tempSetThreadLocal(tl, a);
  }

  static public <A> AutoCloseable tempSetTL(BetterThreadLocal<A> tl, A a) {
    return tempSetThreadLocalIfNecessary(tl, a);
  }

  static public ThreadLocal<Boolean> DynamicObject_loading = or((ThreadLocal) get(getClass("x30_pkg.x30_util"), "DynamicObject_loading"), new ThreadLocal());

  static public ThreadLocal<Boolean> dynamicObjectIsLoading_threadLocal() {
    return DynamicObject_loading;
  }

  static public <T> void sort(T[] a, Comparator<? super T> c) {
    if (a != null)
      Arrays.sort(a, c);
  }

  static public <T> void sort(T[] a) {
    if (a != null)
      Arrays.sort(a);
  }

  static public void sort(int[] a) {
    if (a != null)
      Arrays.sort(a);
  }

  static public <T> void sort(List<T> a, Comparator<? super T> c) {
    if (a != null)
      Collections.sort(a, c);
  }

  static public void sort(List a) {
    if (a != null)
      Collections.sort(a);
  }

  static public Comparator makeComparator(final Object f) {
    if (f instanceof Comparator)
      return (Comparator) f;
    return new Comparator() {

      public int compare(Object a, Object b) {
        return (Integer) callF(f, a, b);
      }
    };
  }

  static public List<String> allMethodNames(Object o) {
    Class c = _getClass(o);
    TreeSet<String> names = new TreeSet();
    while (c != null) {
      for (Method m : c.getDeclaredMethods()) names.add(m.getName());
      c = c.getSuperclass();
    }
    return asList(names);
  }

  static public Throwable printStackTrace2(Throwable e) {
    print(getStackTrace2(e));
    return e;
  }

  static public void printStackTrace2() {
    printStackTrace2(new Throwable());
  }

  static public void printStackTrace2(String msg) {
    printStackTrace2(new Throwable(msg));
  }

  static public Throwable innerException(Throwable e) {
    return getInnerException(e);
  }

  static public Map<Thread, Object> vm_threadInterruptionReasonsMap() {
    return vm_generalWeakSubMap("Thread interruption reasons");
  }

  static public Map vm_generalSubMap(Object name) {
    synchronized (vm_generalMap()) {
      Map map = (Map) (vm_generalMap_get(name));
      if (map == null)
        vm_generalMap_put(name, map = synchroMap());
      return map;
    }
  }

  static public void warnIfOddCount(Object... list) {
    if (odd(l(list)))
      printStackTrace("Odd list size: " + list);
  }

  static public String programIDWithCase() {
    return nempty(caseID()) ? programID() + "/" + quoteUnlessIdentifierOrInteger(caseID()) : programID();
  }

  static public <A, B> Map<A, B> newWeakMap() {
    return newWeakHashMap();
  }

  static public <A> WeakReference<A> newWeakReference(A a) {
    return a == null ? null : new WeakReference(a);
  }

  static public <A> A nextFromIterator(Iterator<A> it) {
    return it == null || !it.hasNext() ? null : it.next();
  }

  static public String collapseWord(String s) {
    if (s == null)
      return "";
    StringBuilder buf = new StringBuilder();
    for (int i = 0; i < l(s); i++) if (i == 0 || !charactersEqualIC(s.charAt(i), s.charAt(i - 1)))
      buf.append(s.charAt(i));
    return buf.toString();
  }

  static public List<String> toLowerCase(List<String> strings) {
    List<String> x = new ArrayList();
    for (String s : strings) x.add(s.toLowerCase());
    return x;
  }

  static public String[] toLowerCase(String[] strings) {
    String[] x = new String[l(strings)];
    for (int i = 0; i < l(strings); i++) x[i] = strings[i].toLowerCase();
    return x;
  }

  static public String toLowerCase(String s) {
    return s == null ? "" : s.toLowerCase();
  }

  static public String firstWord2(String s) {
    s = xltrim(s);
    if (empty(s))
      return "";
    if (isLetterOrDigit(first(s)))
      return takeCharsWhile(__53 -> isLetterOrDigit(__53), s);
    else
      return "" + first(s);
  }

  static public TimeZone getTimeZone(String name) {
    return TimeZone.getTimeZone(name);
  }

  static public String standardTimeZone_name = "Europe/Berlin";

  static public String standardTimeZone() {
    return standardTimeZone_name;
  }

  static public betterCIComparator_C betterCIComparator_instance;

  static public betterCIComparator_C betterCIComparator() {
    if (betterCIComparator_instance == null)
      betterCIComparator_instance = new betterCIComparator_C();
    return betterCIComparator_instance;
  }

  final static public class betterCIComparator_C implements Comparator<String> {

    public int compare(String s1, String s2) {
      if (s1 == null)
        return s2 == null ? 0 : -1;
      if (s2 == null)
        return 1;
      int n1 = s1.length();
      int n2 = s2.length();
      int min = Math.min(n1, n2);
      for (int i = 0; i < min; i++) {
        char c1 = s1.charAt(i);
        char c2 = s2.charAt(i);
        if (c1 != c2) {
          c1 = Character.toUpperCase(c1);
          c2 = Character.toUpperCase(c2);
          if (c1 != c2) {
            c1 = Character.toLowerCase(c1);
            c2 = Character.toLowerCase(c2);
            if (c1 != c2) {
              return c1 - c2;
            }
          }
        }
      }
      return n1 - n2;
    }
  }

  static public boolean isCISet(Iterable<String> l) {
    return l instanceof TreeSet && ((TreeSet) l).comparator() == caseInsensitiveComparator();
  }

  static public String upper(String s) {
    return s == null ? null : s.toUpperCase();
  }

  static public char upper(char c) {
    return Character.toUpperCase(c);
  }

  static public String htmlencode_noQuotes(String s) {
    if (s == null)
      return "";
    int n = s.length();
    StringBuilder out = null;
    for (int i = 0; i < n; i++) {
      char c = s.charAt(i);
      if (c == '<') {
        if (out == null)
          out = new StringBuilder(Math.max(16, n)).append(takeFirst(i, s));
        out.append("&lt;");
      } else if (c == '>') {
        if (out == null)
          out = new StringBuilder(Math.max(16, n)).append(takeFirst(i, s));
        out.append("&gt;");
      } else if (c > 127 || c == '&') {
        int cp = s.codePointAt(i);
        if (out == null)
          out = new StringBuilder(Math.max(16, n)).append(takeFirst(i, s));
        out.append("&#x");
        out.append(intToHex_flexLength(cp));
        out.append(';');
        i += Character.charCount(cp) - 1;
      } else {
        if (out != null)
          out.append(c);
      }
    }
    return out == null ? s : out.toString();
  }

  static public int[] subIntArray(int[] b, int start) {
    return subIntArray(b, start, l(b));
  }

  static public int[] subIntArray(int[] b, int start, int end) {
    start = max(start, 0);
    end = min(end, l(b));
    if (start == 0 && end == l(b))
      return b;
    if (start >= end)
      return new int[0];
    int[] x = new int[end - start];
    System.arraycopy(b, start, x, 0, end - start);
    return x;
  }

  static public int[] subIntArray(int[] a, IntRange r) {
    return r == null ? null : subIntArray(a, r.start, r.end);
  }

  static public short[] subShortArray(short[] b, int start, int end) {
    start = max(start, 0);
    end = min(end, l(b));
    if (start == 0 && end == l(b))
      return b;
    if (start >= end)
      return new short[0];
    short[] x = new short[end - start];
    System.arraycopy(b, start, x, 0, end - start);
    return x;
  }

  static public byte[] subByteArray(byte[] b, int start) {
    return subByteArray(b, start, l(b));
  }

  static public byte[] subByteArray(byte[] b, int start, int end) {
    start = max(start, 0);
    end = min(end, l(b));
    if (start == 0 && end == l(b))
      return b;
    if (start >= end)
      return new byte[0];
    byte[] x = new byte[end - start];
    System.arraycopy(b, start, x, 0, end - start);
    return x;
  }

  static public byte[] subByteArray(byte[] b, IntRange r) {
    return r == null ? null : subByteArray(b, r.start, r.end);
  }

  static public double[] subDoubleArray(double[] b, int start) {
    return subDoubleArray(b, start, l(b));
  }

  static public double[] subDoubleArray(double[] b, int start, int end) {
    start = max(start, 0);
    end = min(end, l(b));
    if (start == 0 && end == l(b))
      return b;
    if (start >= end)
      return new double[0];
    double[] x = new double[end - start];
    System.arraycopy(b, start, x, 0, end - start);
    return x;
  }

  static public Class<?> hotwire(String src) {
    return hotwire(src, __1 -> mainClassNameForClassLoader(__1));
  }

  static public Class<?> hotwire(String src, IF1<ClassLoader, String> calculateMainClass) {
    assertFalse(_inCore());
    Class j = getJavaX();
    if (isAndroid()) {
      synchronized (j) {
        List<File> libraries = new ArrayList<File>();
        File srcDir = (File) call(j, "transpileMain", src, libraries);
        if (srcDir == null)
          throw fail("transpileMain returned null (src=" + quote(src) + ")");
        Object androidContext = get(j, "androidContext");
        return (Class) call(j, "loadx2android", srcDir, src);
      }
    } else {
      Class c = (Class) (call(j, "hotwire", src));
      hotwire_copyOver(c);
      return c;
    }
  }

  static public <A> A callMain(A c, String... args) {
    callOpt(c, "main", new Object[] { args });
    return c;
  }

  static public void callMain() {
    callMain(mc());
  }

  static public ThreadLocal<Boolean> htmlencode_forParams_useV2 = new ThreadLocal();

  static public String htmlencode_forParams(String s) {
    if (s == null)
      return "";
    if (isTrue(htmlencode_forParams_useV2.get()))
      return htmlencode_forParams_v2(s);
    StringBuilder out = new StringBuilder(Math.max(16, s.length()));
    for (int i = 0; i < s.length(); i++) {
      char c = s.charAt(i);
      if (c > 127 || c == '"' || c == '<' || c == '>') {
        out.append("&#");
        out.append((int) c);
        out.append(';');
      } else
        out.append(c);
    }
    return out.toString();
  }

  static public <A> TreeSet<A> treeSet() {
    return new TreeSet();
  }

  static public int subclassDistance(Class a, Class b) {
    int n = 0;
    while (a != b) {
      a = a.getSuperclass();
      if (a == null)
        return Integer.MAX_VALUE;
      ++n;
    }
    return n;
  }

  static public Object realMC() {
    return getThreadLocal(realMC_tl());
  }

  static public List<Field> allFieldObjects_dontMakeAccessible(Object o) {
    List<Field> fields = new ArrayList();
    Class _c = _getClass(o);
    do {
      addAll(fields, _c.getDeclaredFields());
      _c = _c.getSuperclass();
    } while (_c != null);
    return fields;
  }

  static public Pair pairMapB(Object f, Pair p) {
    return p == null ? null : pair(p.a, callF(f, p.b));
  }

  static public <A, B, C> Pair<A, C> pairMapB(IF1<B, C> f, Pair<A, B> p) {
    return p == null ? null : pair(p.a, f.get(p.b));
  }

  static public Pair pairMapB(Pair p, Object f) {
    return pairMap(f, p);
  }

  static public <A, B> Map<B, A> mapToKeys(Iterable<A> l, IF1<A, B> f) {
    if (l == null)
      return null;
    HashMap<B, A> map = new HashMap();
    for (A a : l) map.put(f.get(a), a);
    return map;
  }

  static public <A, B> Map<B, A> mapToKeys(IF1<A, B> f, A[] l) {
    return mapToKeys(f, asList(l));
  }

  static public <A, B> Map<B, A> mapToKeys(IF1<A, B> f, Iterable<A> l) {
    return mapToKeys(l, f);
  }

  static public <A, B> Map<A, B> weakMap() {
    return newWeakHashMap();
  }

  static public <A, B> B getOrCreate(Map<A, B> map, A key, Class<? extends B> c) {
    try {
      B b = map.get(key);
      if (b == null)
        map.put(key, b = c.newInstance());
      return b;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public <A, B> B getOrCreate(Map<A, B> map, A key, Object f) {
    try {
      B b = map.get(key);
      if (b == null)
        map.put(key, b = (B) callF(f));
      return b;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public <A, B> B getOrCreate(IF0<B> f, Map<A, B> map, A key) {
    return getOrCreate(map, key, f);
  }

  static public <A, B> B getOrCreate(Map<A, B> map, A key, IF0<B> f) {
    B b = map.get(key);
    if (b == null)
      map.put(key, b = f.get());
    return b;
  }

  static public <A, B> B getOrCreate(Class<? extends B> c, Map<A, B> map, A key) {
    return getOrCreate(map, key, c);
  }

  static public String toStringOpt(Object o) {
    return o instanceof String ? ((String) o) : null;
  }

  static public String formatSnippetID(String id) {
    return "#" + parseSnippetID(id);
  }

  static public String formatSnippetID(long id) {
    return "#" + id;
  }

  static public <A, B> LinkedHashMap<A, B> syncMapPut2_createLinkedHashMap(LinkedHashMap<A, B> map, A key, B value) {
    if (key != null)
      if (value != null) {
        if (map == null)
          map = new LinkedHashMap();
        synchronized (collectionMutex(map)) {
          map.put(key, value);
        }
      } else if (map != null)
        synchronized (collectionMutex(map)) {
          map.remove(key);
        }
    return map;
  }

  static public boolean fileExists(String path) {
    return path != null && new File(path).exists();
  }

  static public boolean fileExists(File f) {
    return f != null && f.exists();
  }

  static public CloseableIterableIterator emptyCloseableIterableIterator_instance = new CloseableIterableIterator() {

    public Object next() {
      throw fail();
    }

    public boolean hasNext() {
      return false;
    }
  };

  static public <A> CloseableIterableIterator<A> emptyCloseableIterableIterator() {
    return emptyCloseableIterableIterator_instance;
  }

  static public CloseableIterableIterator<String> linesFromReader(Reader r) {
    return linesFromReader(r, null);
  }

  static public CloseableIterableIterator<String> linesFromReader(Reader r, IResourceHolder resourceHolder) {
    final BufferedReader br = bufferedReader(r);
    return holdResource(resourceHolder, iteratorFromFunction_f0_autoCloseable(new F0<String>() {

      public String get() {
        try {
          return readLineFromReaderWithClose(br);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "return readLineFromReaderWithClose(br);";
      }
    }, _wrapIOCloseable(r)));
  }

  static public CloseableIterableIterator<String> linesFromReader(InputStream in) {
    return linesFromReader(inputStreamReader(in));
  }

  static public BufferedReader utf8bufferedReader(InputStream in) {
    try {
      return in == null ? null : bufferedReader(_registerIOWrap(new InputStreamReader(in, "UTF-8"), in));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public BufferedReader utf8bufferedReader(File f) {
    try {
      return utf8bufferedReader(newFileInputStream(f));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static boolean isSnippetID(String s) {
    try {
      parseSnippetID(s);
      return true;
    } catch (RuntimeException e) {
      return false;
    }
  }

  public static long parseSnippetID(String snippetID) {
    long id = Long.parseLong(shortenSnippetID(snippetID));
    if (id == 0)
      throw fail("0 is not a snippet ID");
    return id;
  }

  static public <A> A _registerIOWrap(A wrapper, Object wrapped) {
    return wrapper;
  }

  static public String quickSubstring(String s, int i, int j) {
    if (i >= j)
      return "";
    return s.substring(i, j);
  }

  static public Object defaultDefaultClassFinder() {
    return new F1<String, Class>() {

      public Class get(String name) {
        name = replacePrefix("main$main$", "main$", name);
        Class c = get2(name);
        return c;
      }

      public Class get2(String name) {
        if (eq(name, "<main>"))
          return mc();
        {
          Class c = findClass_fullName(name);
          if (c != null)
            return c;
        }
        if (startsWithAny(name, "loadableUtils.utils$", "main$", mcDollar()))
          for (String pkg : ll("loadableUtils.utils$", mcDollar())) {
            String newName = pkg + afterDollar(name);
            {
              Class c = findClass_fullName(newName);
              if (c != null)
                return c;
            }
          }
        return null;
      }
    };
  }

  static public String dropPrefixSuffix(String prefix, String s) {
    return dropPrefixSuffix(prefix, prefix, s);
  }

  static public String dropPrefixSuffix(String prefix, String suffix, String s) {
    return dropPrefix(prefix, dropSuffix(suffix, s));
  }

  static public short shortFromBytes(byte[] a, int i) {
    return (short) (ubyteToInt(a[i]) << 8 | ubyteToInt(a[i + 1]));
  }

  static public int parseHexChar(char c) {
    if (c >= '0' && c <= '9')
      return charDiff(c, '0');
    if (c >= 'a' && c <= 'f')
      return charDiff(c, 'a') + 10;
    if (c >= 'A' && c <= 'F')
      return charDiff(c, 'A') + 10;
    return -1;
  }

  static public long longFromBytes_bigEndian(byte[] a, int i) {
    return twoIntsToLong_bigEndian(intFromBytes_bigEndian(a, i), intFromBytes_bigEndian(a, i + 4));
  }

  static public <A> TreeMap<String, A> caseInsensitiveMap() {
    return new TreeMap(caseInsensitiveComparator());
  }

  static public Object safeUnstructure(String s) {
    return unstructure(s, true);
  }

  static public Object safeUnstructure(File f) {
    return safeUnstructureGZFile(f);
  }

  static public Class getOuterClass(Class c) {
    return getOuterClass(c, null);
  }

  static public Class getOuterClass(Class c, Object classFinder) {
    try {
      String s = c.getName();
      int i = s.lastIndexOf('$');
      String name = substring(s, 0, i);
      return classForName(name, classFinder);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Class getOuterClass(Object o) {
    return getOuterClass(o, null);
  }

  static public Class getOuterClass(Object o, Object classFinder) {
    return getOuterClass(_getClass(o), classFinder);
  }

  static public HashMap<String, Field> instanceFieldsMap(Object o) {
    return (HashMap) getOpt_getFieldMap(o);
  }

  static public String exceptionToStringShort(Throwable e) {
    try {
      lastException(e);
      e = getInnerException(e);
      String msg = hideCredentials(unnull(e.getMessage()));
      if (msg.indexOf("Error") < 0 && msg.indexOf("Exception") < 0)
        return baseClassName(e) + prependIfNempty(": ", msg);
      else
        return msg;
    } catch (Throwable _e) {
      printStackTrace(_e);
      return "Error in exceptionToStringShort";
    }
  }

  static public Map<Class, Field[]> thisDollarOneFields_cache = newDangerousWeakHashMap();

  static public Field[] thisDollarOneFields(Class c) {
    synchronized (thisDollarOneFields_cache) {
      Field[] l = thisDollarOneFields_cache.get(c);
      if (l == null)
        thisDollarOneFields_cache.put(c, l = thisDollarOneFields_uncached(c));
      return l;
    }
  }

  static public Field[] thisDollarOneFields_uncached(Class c) {
    List<Field> fields = new ArrayList();
    do {
      for (Field f : c.getDeclaredFields()) if (f.getName().startsWith("this$"))
        fields.add(makeAccessible(f));
      c = c.getSuperclass();
    } while (c != null);
    return toArray(new Field[l(fields)], fields);
  }

  static public Map<Class, HashMap<String, Method>> callOpt_noArgs_cache = newDangerousWeakHashMap();

  static public Object callOpt_noArgs(Object o, String method) {
    try {
      if (o == null)
        return null;
      if (o instanceof Class)
        return callOpt(o, method);
      Class c = o.getClass();
      HashMap<String, Method> map;
      synchronized (callOpt_noArgs_cache) {
        map = callOpt_noArgs_cache.get(c);
        if (map == null)
          map = callOpt_noArgs_makeCache(c);
      }
      Method m = map.get(method);
      return m != null ? m.invoke(o) : null;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public HashMap<String, Method> callOpt_noArgs_makeCache(Class c) {
    HashMap<String, Method> map = new HashMap();
    Class _c = c;
    do {
      for (Method m : c.getDeclaredMethods()) if (m.getParameterTypes().length == 0 && !reflection_isForbiddenMethod(m)) {
        makeAccessible(m);
        String name = m.getName();
        if (!map.containsKey(name))
          map.put(name, m);
      }
      _c = _c.getSuperclass();
    } while (_c != null);
    callOpt_noArgs_cache.put(c, map);
    return map;
  }

  static public String[] match2(List<String> pat, List<String> tok) {
    int i = pat.indexOf("...");
    if (i < 0)
      return match2_match(pat, tok);
    pat = new ArrayList<String>(pat);
    pat.set(i, "*");
    while (pat.size() < tok.size()) {
      pat.add(i, "*");
      pat.add(i + 1, "");
    }
    return match2_match(pat, tok);
  }

  static public String[] match2_match(List<String> pat, List<String> tok) {
    List<String> result = new ArrayList<String>();
    if (pat.size() != tok.size()) {
      return null;
    }
    for (int i = 1; i < pat.size(); i += 2) {
      String p = pat.get(i), t = tok.get(i);
      if (eq(p, "*"))
        result.add(t);
      else if (!equalsIgnoreCase(unquote(p), unquote(t)))
        return null;
    }
    return result.toArray(new String[result.size()]);
  }

  static public <A> AutoCloseable tempSetThreadLocalIfNecessary(ThreadLocal<A> tl, A a) {
    if (tl == null)
      return null;
    A prev = tl.get();
    if (eq(prev, a))
      return null;
    tl.set(a);
    return new AutoCloseable() {

      public String toString() {
        return "tl.set(prev);";
      }

      public void close() throws Exception {
        tl.set(prev);
      }
    };
  }

  static public <A> AutoCloseable tempSetThreadLocalIfNecessary(BetterThreadLocal<A> tl, A a) {
    if (tl == null)
      return null;
    A prev = tl.get();
    if (eq(prev, a))
      return null;
    tl.set(a);
    return new AutoCloseable() {

      public String toString() {
        return "tl.set(prev);";
      }

      public void close() throws Exception {
        tl.set(prev);
      }
    };
  }

  static public String getStackTrace2(Throwable e) {
    return hideCredentials(getStackTrace(unwrapTrivialExceptionWraps(e)) + replacePrefix("java.lang.RuntimeException: ", "FAIL: ", hideCredentials(str(innerException2(e)))) + "\n");
  }

  static public Throwable getInnerException(Throwable e) {
    if (e == null)
      return null;
    while (e.getCause() != null) e = e.getCause();
    return e;
  }

  static public Throwable getInnerException(Runnable r) {
    return getInnerException(getException(r));
  }

  static public String quoteUnlessIdentifierOrInteger(String s) {
    return quoteIfNotIdentifierOrInteger(s);
  }

  static public boolean charactersEqualIC(char c1, char c2) {
    if (c1 == c2)
      return true;
    char u1 = Character.toUpperCase(c1);
    char u2 = Character.toUpperCase(c2);
    if (u1 == u2)
      return true;
    return Character.toLowerCase(u1) == Character.toLowerCase(u2);
  }

  static public String xltrim(String s) {
    int i = 0, n = l(s);
    while (i < n && contains(" \t\r\n", s.charAt(i))) ++i;
    return substr(s, i);
  }

  static public boolean isLetterOrDigit(char c) {
    return Character.isLetterOrDigit(c);
  }

  static public String takeCharsWhile(String s, Object pred) {
    int i = 0;
    while (i < l(s) && isTrue(callF(pred, s.charAt(i)))) ++i;
    return substring(s, 0, i);
  }

  static public String takeCharsWhile(IF1<Character, Boolean> f, String s) {
    return takeCharsWhile(s, f);
  }

  static public String intToHex_flexLength(int i) {
    return Integer.toHexString(i);
  }

  static public boolean _inCore() {
    return false;
  }

  static public List hotwire_copyOver_after = synchroList();

  static public void hotwire_copyOver(Class c) {
    for (String field : ll("print_log", "print_silent", "androidContext", "_userHome")) setOptIfNotNull(c, field, getOpt(mc(), field));
    setOptIfNotNull(c, "mainBot", getMainBot());
    setOpt(c, "creator_class", new WeakReference(mc()));
    pcallFAll(hotwire_copyOver_after, c);
  }

  static public String htmlencode_forParams_v2(String s) {
    if (s == null)
      return "";
    StringBuilder out = new StringBuilder(Math.max(16, s.length()));
    for (int i = 0; i < s.length(); i++) {
      char c = s.charAt(i);
      if (c > 127 || c == '"' || c == '<' || c == '>' || c == '&') {
        out.append("&#");
        out.append((int) c);
        out.append(';');
      } else
        out.append(c);
    }
    return out.toString();
  }

  static public ThreadLocal realMC_tl_tl = new ThreadLocal();

  static public ThreadLocal realMC_tl() {
    return realMC_tl_tl;
  }

  static public Pair pairMap(Object f, Pair p) {
    return p == null ? null : pair(callF(f, p.a), callF(f, p.b));
  }

  static public <A> Pair<A, A> pairMap(IF1<A, A> f, Pair<A, A> p) {
    return p == null ? null : pair(callF(f, p.a), callF(f, p.b));
  }

  static public Pair pairMap(Pair p, Object f) {
    return pairMap(f, p);
  }

  static public BufferedReader bufferedReader(Reader r) {
    return bufferedReader(r, 8192);
  }

  static public BufferedReader bufferedReader(Reader r, int bufSize) {
    if (r == null)
      return null;
    return r instanceof BufferedReader ? (BufferedReader) r : _registerIOWrap(new BufferedReader(r, bufSize), r);
  }

  static public BufferedReader bufferedReader(File f) {
    return utf8bufferedReader(f);
  }

  static public <A extends AutoCloseable> A holdResource(IResourceHolder holder, A a) {
    {
      if (holder != null)
        holder.add(a);
    }
    return a;
  }

  static public <A> CloseableIterableIterator<A> iteratorFromFunction_f0_autoCloseable(final F0<A> f, final AutoCloseable closeable) {
    class IFF2 extends CloseableIterableIterator<A> {

      public A a;

      public boolean done = false;

      public boolean hasNext() {
        getNext();
        return !done;
      }

      public A next() {
        getNext();
        if (done)
          throw fail();
        A _a = a;
        a = null;
        return _a;
      }

      public void getNext() {
        if (done || a != null)
          return;
        a = f.get();
        done = a == null;
      }

      public void close() throws Exception {
        if (closeable != null)
          closeable.close();
      }
    }
    ;
    return new IFF2();
  }

  static public String readLineFromReaderWithClose(BufferedReader r) {
    try {
      String s = r.readLine();
      if (s == null)
        r.close();
      return s;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public AutoCloseable _wrapIOCloseable(final AutoCloseable c) {
    return c == null ? null : new AutoCloseable() {

      public String toString() {
        return "c.close();\r\n    _registerIO(c, null, false);";
      }

      public void close() throws Exception {
        c.close();
        _registerIO(c, null, false);
      }
    };
  }

  static public BufferedReader inputStreamReader(InputStream in) {
    return utf8Reader(in);
  }

  static public BufferedReader inputStreamReader(File f) {
    return utf8Reader(f);
  }

  static public FileInputStream newFileInputStream(File path) throws IOException {
    return newFileInputStream(path.getPath());
  }

  static public FileInputStream newFileInputStream(String path) throws IOException {
    FileInputStream f = new FileInputStream(path);
    _registerIO(f, path, true);
    return f;
  }

  static public String shortenSnippetID(String snippetID) {
    if (snippetID.startsWith("#"))
      snippetID = snippetID.substring(1);
    String httpBlaBla = "http://tinybrain.de/";
    if (snippetID.startsWith(httpBlaBla))
      snippetID = snippetID.substring(httpBlaBla.length());
    return "" + parseLong(snippetID);
  }

  static public Object get2(Object o, String field1, String field2) {
    return get(get(o, field1), field2);
  }

  static public boolean startsWithAny(String a, Collection<String> b) {
    for (String prefix : unnullForIteration(b)) if (startsWith(a, prefix))
      return true;
    return false;
  }

  static public boolean startsWithAny(String a, String... b) {
    if (b != null)
      for (String prefix : unnullForIteration(b)) if (startsWith(a, prefix))
        return true;
    return false;
  }

  static public boolean startsWithAny(String a, Collection<String> b, Matches m) {
    for (String prefix : unnullForIteration(b)) if (startsWith(a, prefix, m))
      return true;
    return false;
  }

  static public String mcDollar() {
    return mcName() + "$";
  }

  static public String afterDollar(String s) {
    return substring(s, smartIndexOf(s, '$') + 1);
  }

  static public int charDiff(char a, char b) {
    return (int) a - (int) b;
  }

  static public int charDiff(String a, char b) {
    return charDiff(stringToChar(a), b);
  }

  static public long twoIntsToLong_bigEndian(int a, int b) {
    return twoIntsToLong(a, b);
  }

  static public int intFromBytes_bigEndian(byte[] a, int i) {
    return intFromBytes(a, i);
  }

  static public Object safeUnstructureGZFile(File f) {
    try {
      if (!fileExists(f))
        return null;
      BufferedReader reader = utf8BufferedReader(gzInputStream(f));
      return unstructure_tok(javaTokC_noMLS_onReader(reader), true, null);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public String baseClassName(String className) {
    return substring(className, className.lastIndexOf('.') + 1);
  }

  static public String baseClassName(Object o) {
    return baseClassName(getClassName(o));
  }

  static public String prependIfNempty(String prefix, String s) {
    return empty(s) ? unnull(s) : prefix + s;
  }

  static public boolean equalsIgnoreCase(String a, String b) {
    return eqic(a, b);
  }

  static public boolean equalsIgnoreCase(char a, char b) {
    return eqic(a, b);
  }

  static public Throwable unwrapTrivialExceptionWraps(Throwable e) {
    if (e == null)
      return e;
    while (e.getClass() == RuntimeException.class && e.getCause() != null && eq(e.getMessage(), str(e.getCause()))) e = e.getCause();
    return e;
  }

  static public Throwable innerException2(Throwable e) {
    if (e == null)
      return null;
    while (empty(e.getMessage()) && e.getCause() != null) e = e.getCause();
    return e;
  }

  static public String quoteIfNotIdentifierOrInteger(String s) {
    if (s == null)
      return null;
    return isJavaIdentifier(s) || isInteger(s) ? s : quote(s);
  }

  static public String substr(String s, int x) {
    return substring(s, x);
  }

  static public String substr(String s, int x, int y) {
    return substring(s, x, y);
  }

  static public void setOptIfNotNull(Object o, String field, Object value) {
    if (value != null)
      setOpt(o, field, value);
  }

  static public Object mainBot;

  static public Object getMainBot() {
    return mainBot;
  }

  static public void _registerIO(Object object, String path, boolean opened) {
  }

  static public BufferedReader utf8Reader(InputStream in) {
    return utf8BufferedReader(in);
  }

  static public BufferedReader utf8Reader(File f) {
    return utf8BufferedReader(f);
  }

  static public String mcName() {
    return mc().getName();
  }

  static public char stringToChar(String s) {
    if (l(s) != 1)
      throw fail("bad stringToChar: " + s);
    return firstChar(s);
  }

  static public long twoIntsToLong(int a, int b) {
    return (((long) a) << 32) | (((long) b) & 0xFFFFFFFFL);
  }

  static public int intFromBytes(byte[] a, int i) {
    return ubyteToInt(a[i]) << 24 | ubyteToInt(a[i + 1]) << 16 | ubyteToInt(a[i + 2]) << 8 | ubyteToInt(a[i + 3]);
  }

  static public BufferedReader utf8BufferedReader(InputStream in) {
    return utf8bufferedReader(in);
  }

  static public BufferedReader utf8BufferedReader(File f) {
    return utf8bufferedReader(f);
  }

  static public char firstChar(String s) {
    return s.charAt(0);
  }

  static public interface IResourceHolder {

    public <A extends AutoCloseable> A add(A a);

    public Collection<AutoCloseable> takeAll();
  }

  static public class Chain<A> implements Iterable<A> {

    public A element;

    public Chain<A> next;

    public int size;

    public Chain() {
    }

    public Chain(A element) {
      this.element = element;
      size = 1;
    }

    public Chain(A element, Chain<A> next) {
      this.next = next;
      this.element = element;
      size = next != null ? next.size + 1 : 1;
    }

    public String toString() {
      return str(toList());
    }

    public ArrayList<A> toList() {
      ArrayList<A> l = emptyList(size);
      Chain<A> c = this;
      while (c != null) {
        l.add(c.element);
        c = c.next;
      }
      return l;
    }

    public Iterator<A> iterator() {
      return toList().iterator();
    }
  }

  static public class StructureStringIndenter {

    final public StructureStringIndenter setLevels(int levels) {
      return levels(levels);
    }

    public StructureStringIndenter levels(int levels) {
      this.levels = levels;
      return this;
    }

    final public int getLevels() {
      return levels();
    }

    public int levels() {
      return levels;
    }

    public int levels = 100;

    final public StructureStringIndenter setInlineChars(int inlineChars) {
      return inlineChars(inlineChars);
    }

    public StructureStringIndenter inlineChars(int inlineChars) {
      this.inlineChars = inlineChars;
      return this;
    }

    final public int getInlineChars() {
      return inlineChars();
    }

    public int inlineChars() {
      return inlineChars;
    }

    public int inlineChars = 40;

    final public StructureStringIndenter setVerbose(boolean verbose) {
      return verbose(verbose);
    }

    public StructureStringIndenter verbose(boolean verbose) {
      this.verbose = verbose;
      return this;
    }

    final public boolean getVerbose() {
      return verbose();
    }

    public boolean verbose() {
      return verbose;
    }

    public boolean verbose = false;

    public List<String> tok;

    public Map<Integer, Integer> bracketMap;

    public String get(String s) {
      if (s == null)
        return null;
      tok = javaTokForStructure(s);
      int n = l(tok);
      bracketMap = getBracketMap(tok, __1 -> isOpeningBracket(__1), __2 -> isClosingBracket(__2));
      int levels = clampToInt(this.levels * 2L);
      StringBuilder buf = new StringBuilder();
      int indent = 0;
      for (int i = 0; i < n; i++) {
        String t = tok.get(i);
        if (isOpeningBracket(t)) {
          String prev = _get(tok, i - 2);
          Integer j = or(bracketMap.get(i), n);
          if (j != null && (eq(prev, "intarray") || !tokenRangeLongerThanNChars(tok, i + 1, j + 1, inlineChars))) {
            buf.append(joinSubList(tok, i, j + 1));
            i = j;
          } else {
            if (verbose)
              print("Bracket part longer than " + inlineChars + " chars: " + quote(shortenJoinSubList(inlineChars, tok, i, j + 1)));
            indent += 2;
            buf.append(t);
            if (indent <= levels)
              buf.append("\n").append(spaces(indent));
          }
        } else if (isClosingBracket(t)) {
          indent -= 2;
          if (indent < levels)
            buf.append("\n").append(spaces(indent));
          buf.append(t);
        } else if (indent <= levels && eq(t, ",")) {
          buf.append(t).append("\n").append(spaces(indent));
          i++;
        } else
          buf.append(t);
      }
      return str(buf);
    }
  }

  static public class CompactHashMap<K, V> extends CompactAbstractMap<K, V> {

    final static public int INITIAL_SIZE = 3;

    final static public double LOAD_FACTOR = 0.6;

    final static public Object nullObject = new Object();

    final static public Object deletedObject = new Object();

    public int elements;

    public int freecells;

    public Object[] table;

    public CompactHashMap() {
      this(INITIAL_SIZE);
    }

    public CompactHashMap(int size) {
      table = new Object[(size == 0 ? 1 : size) * 2];
      elements = 0;
      freecells = tableSize();
    }

    public CompactHashMap(Map<K, V> map) {
      this(0);
      if (map != null)
        putAll(map);
    }

    public synchronized int size() {
      return elements;
    }

    public synchronized boolean isEmpty() {
      return elements == 0;
    }

    public synchronized void clear() {
      elements = 0;
      for (int ix = 0; ix < tableSize(); ix++) {
        key(ix, null);
        value(ix, null);
      }
      freecells = tableSize();
    }

    public synchronized boolean containsKey(Object k) {
      return key(findKeyIndex(k)) != null;
    }

    public synchronized boolean containsValue(Object v) {
      if (v == null)
        v = (V) nullObject;
      for (int ix = 0; ix < tableSize(); ix++) if (value(ix) != null && value(ix).equals(v))
        return true;
      return false;
    }

    public synchronized Set<Entry<K, V>> entrySet() {
      return new EntrySet();
    }

    public synchronized V remove(Object k) {
      int index = findKeyIndex(k);
      if (key(index) != null) {
        V v = value(index);
        key(index, deletedObject);
        value(index, deletedObject);
        elements--;
        return v;
      } else
        return null;
    }

    public synchronized V put(K k, V v) {
      if (k == null)
        k = (K) nullObject;
      int hash = k.hashCode();
      int index = (hash & 0x7FFFFFFF) % tableSize();
      int offset = 1;
      int deletedix = -1;
      while (key(index) != null && !(key(index).hashCode() == hash && key(index).equals(k))) {
        if (key(index) == deletedObject)
          deletedix = index;
        index = ((index + offset) & 0x7FFFFFFF) % tableSize();
        offset = offset * 2 + 1;
        if (offset == -1)
          offset = 2;
      }
      if (key(index) == null) {
        if (deletedix != -1)
          index = deletedix;
        else
          freecells--;
        elements++;
        key(index, k);
        value(index, v);
        if (1 - (freecells / (double) tableSize()) > LOAD_FACTOR)
          rehash(tableSize() * 2 + 1);
        return null;
      } else {
        V oldv = value(index);
        value(index, v);
        return oldv;
      }
    }

    public void rehash(int newCapacity) {
      int oldCapacity = tableSize();
      Object[] newTable = new Object[newCapacity * 2];
      for (int ix = 0; ix < oldCapacity; ix++) {
        Object k = key(ix);
        if (k == null || k == deletedObject)
          continue;
        int hash = k.hashCode();
        int index = (hash & 0x7FFFFFFF) % newCapacity;
        int offset = 1;
        while (newTable[index * 2] != null) {
          index = ((index + offset) & 0x7FFFFFFF) % newCapacity;
          offset = offset * 2 + 1;
          if (offset == -1)
            offset = 2;
        }
        newTable[index * 2] = k;
        newTable[index * 2 + 1] = value(ix);
      }
      table = newTable;
      freecells = tableSize() - elements;
    }

    public synchronized V get(Object k) {
      return value(findKeyIndex(k));
    }

    public synchronized Collection<V> values() {
      return new ValueCollection();
    }

    public synchronized Set<K> keySet() {
      return new KeySet();
    }

    final public int findKeyIndex(Object k) {
      if (k == null)
        k = nullObject;
      int hash = k.hashCode();
      int index = (hash & 0x7FFFFFFF) % tableSize();
      int offset = 1;
      while (key(index) != null && !(key(index).hashCode() == hash && key(index).equals(k))) {
        index = ((index + offset) & 0x7FFFFFFF) % tableSize();
        offset = offset * 2 + 1;
        if (offset == -1)
          offset = 2;
      }
      return index;
    }

    public class KeySet extends AbstractSet<K> {

      public int size() {
        synchronized (CompactHashMap.this) {
          return elements;
        }
      }

      public boolean contains(Object k) {
        synchronized (CompactHashMap.this) {
          return containsKey(k);
        }
      }

      public Iterator<K> iterator() {
        synchronized (CompactHashMap.this) {
          return new KeyIterator();
        }
      }
    }

    public class KeyIterator<K> implements Iterator<K> {

      public int ix;

      public KeyIterator() {
        synchronized (CompactHashMap.this) {
          for (; ix < tableSize(); ix++) if (value(ix) != null && key(ix) != deletedObject)
            break;
        }
      }

      public boolean hasNext() {
        synchronized (CompactHashMap.this) {
          return ix < tableSize();
        }
      }

      public void remove() {
        throw new UnsupportedOperationException("Collection is read-only");
      }

      public K next() {
        synchronized (CompactHashMap.this) {
          if (ix >= tableSize())
            throw new NoSuchElementException();
          K key = (K) key(ix++);
          for (; ix < tableSize(); ix++) if (key(ix) != null && key(ix) != deletedObject)
            break;
          return key;
        }
      }
    }

    public class EntrySet extends AbstractSet<Map.Entry<K, V>> {

      public int size() {
        synchronized (CompactHashMap.this) {
          return elements;
        }
      }

      public boolean contains(Object o) {
        synchronized (CompactHashMap.this) {
          if (o instanceof Map.Entry) {
            Object key = ((Map.Entry) o).getKey();
            if (!containsKey((Map.Entry) o))
              return false;
            return eq(((Map.Entry) o).getValue(), get(key));
          }
          return false;
        }
      }

      public Iterator<Map.Entry<K, V>> iterator() {
        return new EntryIterator();
      }
    }

    public class EntryIterator implements Iterator<Map.Entry<K, V>> {

      public int ix;

      public EntryIterator() {
        synchronized (CompactHashMap.this) {
          for (; ix < tableSize(); ix++) if (value(ix) != null && key(ix) != deletedObject)
            break;
        }
      }

      public boolean hasNext() {
        synchronized (CompactHashMap.this) {
          return ix < tableSize();
        }
      }

      public void remove() {
        throw new UnsupportedOperationException("Collection is read-only");
      }

      public Map.Entry<K, V> next() {
        synchronized (CompactHashMap.this) {
          if (ix >= tableSize())
            throw new NoSuchElementException();
          K key = key(ix);
          V val = value(ix);
          ++ix;
          for (; ix < tableSize(); ix++) if (key(ix) != null && key(ix) != deletedObject)
            break;
          return simpleMapEntry(key, val);
        }
      }
    }

    public class ValueCollection<V> extends AbstractCollection<V> {

      public int size() {
        synchronized (CompactHashMap.this) {
          return elements;
        }
      }

      public Iterator<V> iterator() {
        return new ValueIterator();
      }

      public boolean contains(Object v) {
        return containsValue(v);
      }
    }

    public class ValueIterator<V> implements Iterator<V> {

      public int ix;

      public ValueIterator() {
        synchronized (CompactHashMap.this) {
          for (; ix < table.length / 2; ix++) if (value(ix) != null && value(ix) != deletedObject)
            break;
        }
      }

      public boolean hasNext() {
        synchronized (CompactHashMap.this) {
          return ix < tableSize();
        }
      }

      public void remove() {
        throw new UnsupportedOperationException("Collection is read-only");
      }

      public V next() {
        synchronized (CompactHashMap.this) {
          if (ix >= tableSize())
            throw new NoSuchElementException();
          V value = (V) value(ix++);
          for (; ix < tableSize(); ix++) if (value(ix) != null && value(ix) != deletedObject)
            break;
          return value;
        }
      }
    }

    public K key(int i) {
      return (K) table[i * 2];
    }

    public void key(int i, Object key) {
      table[i * 2] = key;
    }

    public V value(int i) {
      return (V) table[i * 2 + 1];
    }

    public void value(int i, Object value) {
      table[i * 2 + 1] = value;
    }

    public int tableSize() {
      return table.length / 2;
    }
  }

  static public class DefunctClassLoader {
  }

  static public class BetterThread extends Thread {

    public Runnable target;

    public BetterThread(Runnable target) {
      this.target = target;
      _created();
    }

    public BetterThread(Runnable target, String name) {
      super(name);
      this.target = target;
      _created();
    }

    public void _created() {
      vmBus_send("threadCreated", this);
    }

    public void run() {
      try {
        try {
          vmBus_send("threadStarted", this);
          if (target != null)
            target.run();
        } finally {
          vmBus_send("threadEnded", this);
        }
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public Runnable getTarget() {
      return target;
    }
  }

  static public interface ITokCondition {

    public boolean get(List<String> tok, int i);
  }

  static public interface ISetAndGet<A> extends ISetter<A>, IF0<A> {
  }

  static abstract public class TokCondition implements ITokCondition {

    public abstract boolean get(List<String> tok, int i);
  }

  static public interface IContentsIndexedList<A> extends List<A> {

    public int[] indicesOf(A o);
  }

  static public class IntVar {

    public int a;

    public synchronized void set(int v) {
      if (v != a) {
        a = v;
        notifyAll();
      }
    }

    public synchronized int get() {
      return a;
    }

    synchronized public int waitForValue(int x) {
      try {
        while (a != x) wait();
        return a;
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public String toString() {
      return str(this.get());
    }
  }

  static final public class WidthAndHeightFinal extends Meta implements WidthAndHeight, IFieldsToList {

    static final public String _fieldOrder = "width height";

    public int width;

    public int height;

    public WidthAndHeightFinal() {
    }

    public WidthAndHeightFinal(int width, int height) {
      this.height = height;
      this.width = width;
    }

    public boolean equals(Object o) {
      if (!(o instanceof WidthAndHeightFinal))
        return false;
      WidthAndHeightFinal __0 = (WidthAndHeightFinal) o;
      return width == __0.width && height == __0.height;
    }

    public int hashCode() {
      int h = -1177452162;
      h = boostHashCombine(h, _hashCode(width));
      h = boostHashCombine(h, _hashCode(height));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { width, height };
    }

    public int getWidth() {
      return width;
    }

    public int getHeight() {
      return height;
    }

    public WidthAndHeightFinal(int wAndH) {
      this(wAndH, wAndH);
    }

    public String toString() {
      return n2(width) + "*" + n2(height) + " px";
    }
  }

  static public class SynchronizedNavigableMap<K, V> extends SynchronizedSortedMap<K, V> implements NavigableMap<K, V> {

    public SynchronizedNavigableMap() {
    }

    public NavigableMap<K, V> innerMap() {
      return (NavigableMap) m;
    }

    public SynchronizedNavigableMap(NavigableMap<K, V> m) {
      super(m);
    }

    public SynchronizedNavigableMap(NavigableMap<K, V> m, Object mutex) {
      super(m, mutex);
    }

    public Entry<K, V> lowerEntry(K key) {
      synchronized (mutex) {
        return innerMap().lowerEntry(key);
      }
    }

    public K lowerKey(K key) {
      synchronized (mutex) {
        return innerMap().lowerKey(key);
      }
    }

    public Entry<K, V> floorEntry(K key) {
      synchronized (mutex) {
        return innerMap().floorEntry(key);
      }
    }

    public K floorKey(K key) {
      synchronized (mutex) {
        return innerMap().floorKey(key);
      }
    }

    public Entry<K, V> ceilingEntry(K key) {
      synchronized (mutex) {
        return innerMap().ceilingEntry(key);
      }
    }

    public K ceilingKey(K key) {
      synchronized (mutex) {
        return innerMap().ceilingKey(key);
      }
    }

    public Entry<K, V> higherEntry(K key) {
      synchronized (mutex) {
        return innerMap().higherEntry(key);
      }
    }

    public K higherKey(K key) {
      synchronized (mutex) {
        return innerMap().higherKey(key);
      }
    }

    public Entry<K, V> firstEntry() {
      synchronized (mutex) {
        return innerMap().firstEntry();
      }
    }

    public Entry<K, V> lastEntry() {
      synchronized (mutex) {
        return innerMap().lastEntry();
      }
    }

    public Entry<K, V> pollFirstEntry() {
      synchronized (mutex) {
        return innerMap().pollFirstEntry();
      }
    }

    public Entry<K, V> pollLastEntry() {
      synchronized (mutex) {
        return innerMap().pollLastEntry();
      }
    }

    public NavigableMap<K, V> descendingMap() {
      synchronized (mutex) {
        return new SynchronizedNavigableMap<>(innerMap().descendingMap(), mutex);
      }
    }

    public NavigableSet<K> keySet() {
      return navigableKeySet();
    }

    public NavigableSet<K> navigableKeySet() {
      synchronized (mutex) {
        return new SynchronizedNavigableSet<>(innerMap().navigableKeySet(), mutex);
      }
    }

    public NavigableSet<K> descendingKeySet() {
      synchronized (mutex) {
        return new SynchronizedNavigableSet<>(innerMap().descendingKeySet(), mutex);
      }
    }

    public SortedMap<K, V> subMap(K fromKey, K toKey) {
      synchronized (mutex) {
        return new SynchronizedNavigableMap<>(innerMap().subMap(fromKey, true, toKey, false), mutex);
      }
    }

    public SortedMap<K, V> headMap(K toKey) {
      synchronized (mutex) {
        return new SynchronizedNavigableMap<>(innerMap().headMap(toKey, false), mutex);
      }
    }

    public SortedMap<K, V> tailMap(K fromKey) {
      synchronized (mutex) {
        return new SynchronizedNavigableMap<>(innerMap().tailMap(fromKey, true), mutex);
      }
    }

    public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
      synchronized (mutex) {
        return new SynchronizedNavigableMap<>(innerMap().subMap(fromKey, fromInclusive, toKey, toInclusive), mutex);
      }
    }

    public NavigableMap<K, V> headMap(K toKey, boolean inclusive) {
      synchronized (mutex) {
        return new SynchronizedNavigableMap<>(innerMap().headMap(toKey, inclusive), mutex);
      }
    }

    public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) {
      synchronized (mutex) {
        return new SynchronizedNavigableMap<>(innerMap().tailMap(fromKey, inclusive), mutex);
      }
    }
  }

  static public class MRUCache<A, B> extends LinkedHashMap<A, B> {

    public int maxSize = 10;

    public MRUCache() {
    }

    public MRUCache(int maxSize) {
      this.maxSize = maxSize;
    }

    public boolean removeEldestEntry(Map.Entry eldest) {
      return size() > maxSize;
    }

    public Object _serialize() {
      return ll(maxSize, cloneLinkedHashMap(this));
    }

    static public MRUCache _deserialize(List l) {
      MRUCache m = new MRUCache();
      m.maxSize = (int) first(l);
      m.putAll((LinkedHashMap) second(l));
      return m;
    }
  }

  static public int concepts_internStringsLongerThan = 10;

  static public ThreadLocal<Boolean> concepts_unlisted = new ThreadLocal();

  static public boolean concepts_unlistedByDefault = true;

  public interface IConceptIndex {

    public void update(Concept c);

    public void remove(Concept c);
  }

  public interface IFieldIndex<A extends Concept, Val> {

    public Collection<A> getAll(Val val);

    public List<Val> allValues();

    public MultiSet<Val> allValues_multiSet();

    public IterableIterator<A> objectIterator();
  }

  static public class ConceptsChange {
  }

  static public class ConceptCreate extends ConceptsChange implements IFieldsToList {

    public Concept c;

    public ConceptCreate() {
    }

    public ConceptCreate(Concept c) {
      this.c = c;
    }

    public String toString() {
      return shortClassName_dropNumberPrefix(this) + "(" + c + ")";
    }

    public boolean equals(Object o) {
      if (!(o instanceof ConceptCreate))
        return false;
      ConceptCreate __3 = (ConceptCreate) o;
      return eq(c, __3.c);
    }

    public int hashCode() {
      int h = -1751266972;
      h = boostHashCombine(h, _hashCode(c));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { c };
    }
  }

  static public class ConceptChange extends ConceptsChange implements IFieldsToList {

    public Concept c;

    public ConceptChange() {
    }

    public ConceptChange(Concept c) {
      this.c = c;
    }

    public String toString() {
      return shortClassName_dropNumberPrefix(this) + "(" + c + ")";
    }

    public boolean equals(Object o) {
      if (!(o instanceof ConceptChange))
        return false;
      ConceptChange __4 = (ConceptChange) o;
      return eq(c, __4.c);
    }

    public int hashCode() {
      int h = -1760609256;
      h = boostHashCombine(h, _hashCode(c));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { c };
    }
  }

  static public class ConceptDelete extends ConceptsChange implements IFieldsToList {

    static final public String _fieldOrder = "id c";

    public long id;

    public Concept c;

    public ConceptDelete() {
    }

    public ConceptDelete(long id, Concept c) {
      this.c = c;
      this.id = id;
    }

    public String toString() {
      return shortClassName_dropNumberPrefix(this) + "(" + id + ", " + c + ")";
    }

    public boolean equals(Object o) {
      if (!(o instanceof ConceptDelete))
        return false;
      ConceptDelete __5 = (ConceptDelete) o;
      return id == __5.id && eq(c, __5.c);
    }

    public int hashCode() {
      int h = -1734431213;
      h = boostHashCombine(h, _hashCode(id));
      h = boostHashCombine(h, _hashCode(c));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { id, c };
    }
  }

  static public class FullChange extends ConceptsChange implements IFieldsToList {

    public FullChange() {
    }

    public String toString() {
      return shortClassName_dropNumberPrefix(this);
    }

    public boolean equals(Object o) {
      return o instanceof FullChange;
    }

    public int hashCode() {
      int h = 733452095;
      return h;
    }

    public Object[] _fieldsToList() {
      return null;
    }
  }

  static public class Concepts implements AutoCloseable {

    public NavigableMap<Long, Concept> concepts = synchroTreeMap();

    final public long getIdCounter() {
      return idCounter();
    }

    public long idCounter() {
      return idCounter;
    }

    public long idCounter;

    transient public HashMap<Class, Object> perClassData;

    transient public Map miscMap;

    transient public String programID;

    transient public File conceptsFile;

    transient public Concepts parent;

    transient volatile public long changes, changesWritten, lastChange;

    transient volatile public java.util.Timer autoSaver;

    transient volatile public boolean dontSave = false;

    transient volatile public boolean savingConcepts, noXFullGrab;

    transient public boolean vmBusSend = true;

    transient public boolean initialSave = false;

    transient public int autoSaveInterval = -1000;

    transient public boolean useGZIP = true, quietSave;

    transient public ReentrantLock lock = new ReentrantLock(true);

    transient public ReentrantLock saverLock = new ReentrantLock(true);

    transient public long lastSaveTook = -1, lastSaveWas, loadTook, uncompressedSize;

    transient public float maxAutoSavePercentage = 10;

    transient public List<IConceptIndex> conceptIndices;

    transient public Map<Class<? extends Concept>, Map<String, IFieldIndex>> fieldIndices;

    transient public Map<Class<? extends Concept>, Map<String, IFieldIndex>> ciFieldIndices;

    transient public List<Runnable> preSave;

    transient public Object classFinder = _defaultClassFinder();

    transient public List onAllChanged = synchroList();

    transient public Set<IVF1> onChange = new HashSet();

    transient public Object saveWrapper;

    final public Concepts setModifyOnCreate(boolean modifyOnCreate) {
      return modifyOnCreate(modifyOnCreate);
    }

    public Concepts modifyOnCreate(boolean modifyOnCreate) {
      this.modifyOnCreate = modifyOnCreate;
      return this;
    }

    final public boolean getModifyOnCreate() {
      return modifyOnCreate();
    }

    public boolean modifyOnCreate() {
      return modifyOnCreate;
    }

    transient public boolean modifyOnCreate = false;

    transient public boolean modifyOnBackRef = false;

    transient public boolean useFileLock = true;

    final public Concepts setGrabThroughSocket(boolean grabThroughSocket) {
      return grabThroughSocket(grabThroughSocket);
    }

    public Concepts grabThroughSocket(boolean grabThroughSocket) {
      this.grabThroughSocket = grabThroughSocket;
      return this;
    }

    final public boolean getGrabThroughSocket() {
      return grabThroughSocket();
    }

    public boolean grabThroughSocket() {
      return grabThroughSocket;
    }

    transient public boolean grabThroughSocket = true;

    transient public FileBasedLock fileLock;

    transient public boolean storeBaseClassesInStructure = false;

    transient public boolean useBackRefsForSearches = false;

    transient public boolean defunct = false;

    transient public int newBackupEveryXMinutes = 60;

    final public Concepts setCloseConceptsOnExit(boolean closeConceptsOnExit) {
      return closeConceptsOnExit(closeConceptsOnExit);
    }

    public Concepts closeConceptsOnExit(boolean closeConceptsOnExit) {
      this.closeConceptsOnExit = closeConceptsOnExit;
      return this;
    }

    final public boolean getCloseConceptsOnExit() {
      return closeConceptsOnExit();
    }

    public boolean closeConceptsOnExit() {
      return closeConceptsOnExit;
    }

    transient public boolean closeConceptsOnExit = false;

    final public Concepts setSaveError(Throwable saveError) {
      return saveError(saveError);
    }

    public Concepts saveError(Throwable saveError) {
      this.saveError = saveError;
      return this;
    }

    final public Throwable getSaveError() {
      return saveError();
    }

    public Throwable saveError() {
      return saveError;
    }

    transient public Throwable saveError;

    transient public Set<IVF1<Throwable>> onSaveError;

    public Concepts onSaveError(IVF1<Throwable> f) {
      onSaveError = createOrAddToSyncLinkedHashSet(onSaveError, f);
      return this;
    }

    public Concepts removeSaveErrorListener(IVF1<Throwable> f) {
      main.remove(onSaveError, f);
      return this;
    }

    public void fireSaveError(Throwable error) {
      if (onSaveError != null)
        for (var listener : onSaveError) pcallF_typed(listener, error);
    }

    public Concepts() {
    }

    public Concepts(String programID) {
      this.programID = programID;
    }

    public Concepts(File conceptsFile) {
      this.conceptsFile = conceptsFile;
    }

    synchronized public long internalID() {
      do {
        ++idCounter;
      } while (hasConcept(idCounter));
      return idCounter;
    }

    synchronized public HashMap<Class, Object> perClassData() {
      if (perClassData == null)
        perClassData = new HashMap();
      return perClassData;
    }

    public void initProgramID() {
      if (programID == null)
        programID = getDBProgramID();
    }

    public Concepts load(String structure) {
      return load(structure, false);
    }

    public Concepts load(String structure, boolean allDynamic) {
      clearConcepts();
      Map<Long, Concept> map = unstructureMap(structure, allDynamic, classFinder);
      concepts.putAll(map);
      assignConceptsToUs();
      calcIdCounter();
      return this;
    }

    public Concepts load() {
      initProgramID();
      Object dbGrabber = miscMapGet("dbGrabber");
      if (dbGrabber != null && !isFalse(callF(dbGrabber)))
        return this;
      try {
        if (grabThroughSocket && tryToGrab())
          return this;
      } catch (Throwable e) {
        if (!exceptionMessageContains(e, "no xfullgrab"))
          printShortException(e);
        print("xfullgrab failed - loading DB of " + programID + " from disk");
      }
      return loadFromDisk();
    }

    public Concepts loadFromDisk() {
      if (nempty(concepts))
        clearConcepts();
      long time = now();
      Map<Long, Concept> _concepts = (Map<Long, Concept>) (unstructureGZFile(conceptsFile(), toIF1(classFinder)));
      putAll(concepts, _concepts);
      assignConceptsToUs();
      loadTook = now() - time;
      done("Loaded " + n2(l(concepts), "concept"), time);
      calcIdCounter();
      return this;
    }

    public Concepts loadConcepts() {
      return load();
    }

    public boolean tryToGrab() {
      if (sameSnippetID(programID, getDBProgramID()))
        return false;
      RemoteDB db = connectToDBOpt(programID);
      try {
        if (db != null) {
          loadGrab(db.fullgrab());
          return true;
        }
        return false;
      } finally {
        _close(db);
      }
    }

    public Concepts loadGrab(String grab) {
      clearConcepts();
      DynamicObject_loading.set(true);
      try {
        Map<Long, Concept> map = (Map) unstructure(grab, false, classFinder);
        concepts.putAll(map);
        assignConceptsToUs();
        for (long l : map.keySet()) idCounter = max(idCounter, l);
      } finally {
        DynamicObject_loading.set(null);
      }
      return this;
    }

    public void assignConceptsToUs() {
      for (Pair<Long, Object> p : mapToPairs((Map<Long, Object>) (Map) concepts)) if (!(p.b instanceof Concept)) {
        print("DROPPING non-existant concept " + p.a + ": " + dynShortName(p.b));
        concepts.remove(p.a);
      }
      for (Concept c : values(concepts)) c._concepts = this;
      for (Concept c : values(concepts)) c._doneLoading2();
    }

    public String progID() {
      return programID == null ? getDBProgramID() : programID;
    }

    public Concept getConcept(String id) {
      return empty(id) ? null : getConcept(parseLong(id));
    }

    public Concept getConcept(long id) {
      return (Concept) concepts.get((long) id);
    }

    public Concept getConcept(RC ref) {
      return ref == null ? null : getConcept(ref.longID());
    }

    public boolean hasConcept(long id) {
      return concepts.containsKey((long) id);
    }

    public void deleteConcept(long id) {
      Concept c = getConcept(id);
      if (c == null)
        print("Concept " + id + " not found");
      else
        c.delete();
    }

    public void calcIdCounter() {
      Long lastID = lastKey(concepts);
      idCounter = lastID == null ? 1 : lastID + 1;
    }

    public File conceptsDir() {
      return dirOfFile(conceptsFile());
    }

    public Concepts conceptsFile(File conceptsFile) {
      this.conceptsFile = conceptsFile;
      return this;
    }

    public File conceptsFile() {
      if (conceptsFile != null)
        return conceptsFile;
      return getProgramFile(programID, useGZIP ? "concepts.structure.gz" : "concepts.structure");
    }

    public File lockFile() {
      return newFile(conceptsDir(), "concepts.lock");
    }

    public FileBasedLock fileLock() {
      if (fileLock == null)
        fileLock = new FileBasedLock(lockFile());
      return fileLock;
    }

    public void saveConceptsIfDirty() {
      saveConcepts();
    }

    public void save() {
      saveConcepts();
    }

    public void saveConcepts() {
      vmBus_send("saveConceptsCalled", Concepts.this);
      if (dontSave)
        return;
      initProgramID();
      saverLock.lock();
      savingConcepts = true;
      long start = now(), time;
      try {
        String s = null;
        long _changes = changes;
        if (_changes == changesWritten)
          return;
        File f = conceptsFile();
        lock.lock();
        long fullTime = now();
        try {
          if (useGZIP) {
            vmBus_send("callingSaveWrapper", Concepts.this, saveWrapper);
            callRunnableWithWrapper(saveWrapper, new Runnable() {

              public void run() {
                try {
                  vmBus_send("callingPreSave", Concepts.this, preSave);
                  callFAll(preSave);
                  vmBus_send("writingFile", Concepts.this, f);
                  uncompressedSize = saveGZStructureToFile(f, cloneMap(concepts), makeStructureData());
                  vmBus_send("gzFileSaved", Concepts.this, f, uncompressedSize);
                } catch (Exception __e) {
                  throw rethrow(__e);
                }
              }

              public String toString() {
                return "vmBus_send callingPreSave(Concepts.this, preSave);\r\n            callFAll(preS...";
              }
            });
            newFile(conceptsDir(), "concepts.structure").delete();
          } else
            s = fullStructure();
        } finally {
          lock.unlock();
        }
        changesWritten = _changes;
        if (!useGZIP) {
          time = now() - start;
          if (!quietSave)
            print("Saving " + toM(l(s)) + "M chars (" + time + " ms)");
          start = now();
          saveTextFile(f, javaTokWordWrap(s));
          newFile(conceptsDir(), "concepts.structure.gz").delete();
        }
        File conceptsFile = conceptsFile();
        File backupFile = newFile(conceptsDir(), "backups/" + fileName(conceptsFile) + ".backup" + ymd() + "-" + formatInt(hours(), 2) + (newBackupEveryXMinutes >= 60 ? "" : formatInt(roundDownTo_rev(minutes(), newBackupEveryXMinutes), 2)));
        copyFile(f, backupFile);
        time = now() - start;
        if (!quietSave)
          print("Saved " + toK(f.length()) + " K, " + n(concepts, "concepts") + " (" + time + " ms)");
        lastSaveWas = fullTime;
        lastSaveTook = now() - fullTime;
        saveError(null);
      } finally {
        savingConcepts = false;
        saverLock.unlock();
      }
    }

    public void _autoSaveConcepts() {
      if (autoSaveInterval < 0 && maxAutoSavePercentage != 0) {
        long pivotTime = Math.round(lastSaveWas + lastSaveTook * 100.0 / maxAutoSavePercentage);
        if (now() < pivotTime) {
          return;
        }
      }
      try {
        saveConcepts();
      } catch (Throwable e) {
        saveError(e);
        print("Concept save failed, will try again");
        printStackTrace(e);
        fireSaveError(e);
      }
    }

    public String fullStructure() {
      return structure(cloneMap(concepts), makeStructureData());
    }

    transient public IF0<structure_Data> makeStructureData;

    public structure_Data makeStructureData() {
      return makeStructureData != null ? makeStructureData.get() : makeStructureData_base();
    }

    final public structure_Data makeStructureData_fallback(IF0<structure_Data> _f) {
      return _f != null ? _f.get() : makeStructureData_base();
    }

    public structure_Data makeStructureData_base() {
      return finishStructureData(new structure_Data());
    }

    public structure_Data finishStructureData(structure_Data data) {
      if (storeBaseClassesInStructure)
        data.storeBaseClasses = true;
      return data;
    }

    public void clearConcepts() {
      for (Concept c : allConcepts()) c.delete();
    }

    public void fireLegacyChangeEvent() {
      synchronized (this) {
        ++changes;
        lastChange = sysNow();
      }
      if (vmBusSend)
        vmBus_send("conceptsChanged", this);
      pcallFAll(onAllChanged);
    }

    synchronized public void autoSaveConcepts() {
      if (autoSaver == null) {
        if (isTransient())
          throw fail("Can't persist transient database");
        autoSaver = doEvery_daemon("Concepts Saver for " + conceptsDir(), abs(autoSaveInterval), new Runnable() {

          public void run() {
            try {
              _autoSaveConcepts();
            } catch (Exception __e) {
              throw rethrow(__e);
            }
          }

          public String toString() {
            return "_autoSaveConcepts()";
          }
        });
      }
    }

    public void close() {
      cleanMeUp();
    }

    public void cleanMeUp() {
      try {
        if (closeConceptsOnExit)
          closeAllOpt(allConcepts());
        defunct = true;
        boolean shouldSave = autoSaver != null;
        if (autoSaver != null) {
          autoSaver.cancel();
          autoSaver = null;
        }
        while (savingConcepts) sleepInCleanUp(10);
        if (shouldSave)
          saveConceptsIfDirty();
      } catch (Throwable __e) {
        pcallFail(__e);
      }
      {
        cleanUp(fileLock);
        fileLock = null;
      }
    }

    public Map<Long, String> getIDsAndNames() {
      Map<Long, String> map = new HashMap();
      Map<Long, Concept> cloned = cloneMap(concepts);
      for (long id : keys(cloned)) map.put(id, cloned.get(id).className);
      return map;
    }

    public void deleteConcepts(List l) {
      ping();
      if (l != null)
        for (Object o : cloneList(l)) if (o instanceof Long) {
          Concept c = concepts.get(o);
          if (c != null)
            c.delete();
        } else if (o instanceof Concept)
          ((Concept) o).delete();
        else
          warn("Can't delete " + getClassName(o));
    }

    public <A extends Concept> A conceptOfType(Class<A> type) {
      IConceptCounter counter = conceptCounterForClass(type);
      if (counter != null)
        return (A) first(counter.allConcepts());
      return firstOfType(allConcepts(), type);
    }

    public <A extends Concept> List<A> conceptsOfType(Class<A> type) {
      List<A> l = conceptsOfType_noParent(type);
      if (parent == null)
        return l;
      return concatLists_conservative(l, parent.conceptsOfType(type));
    }

    public <A extends Concept> List<A> conceptsOfType_noParent(Class<A> type) {
      ping();
      IConceptCounter counter = conceptCounterForClass(type);
      if (counter != null)
        return (List<A>) cloneList(counter.allConcepts());
      return filterByType(allConcepts(), type);
    }

    public <A extends Concept> List<A> listConcepts(Class<A> type) {
      return conceptsOfType(type);
    }

    public <A extends Concept> List<A> list(Class<A> type) {
      return conceptsOfType(type);
    }

    public <A extends Concept> List<A> list_noParent(Class<A> type) {
      return conceptsOfType_noParent(type);
    }

    public List<Concept> list(String type) {
      return conceptsOfType(type);
    }

    public List<Concept> conceptsOfType(String type) {
      return filterByDynamicType(allConcepts(), "main$" + type);
    }

    public boolean hasConceptOfType(Class<? extends Concept> type) {
      return hasType(allConcepts(), type);
    }

    public void persistConcepts() {
      loadConcepts();
      autoSaveConcepts();
    }

    public void conceptPersistence() {
      persistConcepts();
    }

    public Concepts persist() {
      persistConcepts();
      return this;
    }

    public void persist(Integer interval) {
      if (interval != null)
        autoSaveInterval = interval;
      persist();
    }

    public <A extends Concept> A ensureHas(Class<A> c, Runnable r) {
      A a = conceptOfType(c);
      if (a == null) {
        r.run();
        a = conceptOfType(c);
        if (a == null)
          throw fail("Concept not made by " + r + ": " + shortClassName(c));
      }
      return a;
    }

    public void ensureHas(Class<? extends Concept> c1, Class<? extends Concept> c2, Object func) {
      for (Concept a : conceptsOfType(c1)) {
        Concept b = findBackRef(a, c2);
        if (b == null) {
          callF(func, a);
          b = findBackRef(a, c2);
          if (b == null)
            throw fail("Concept not made by " + func + ": " + shortClassName(c2));
        }
      }
    }

    public void forEvery(Class<? extends Concept> type, Object func) {
      for (Concept c : conceptsOfType(type)) callF(func, c);
    }

    public int deleteAll(Class<? extends Concept> type) {
      List<Concept> l = (List) conceptsOfType(type);
      for (Concept c : l) c.delete();
      return l(l);
    }

    public Collection<Concept> allConcepts() {
      synchronized (concepts) {
        return new ArrayList(values(concepts));
      }
    }

    public IConceptCounter conceptCounterForClass(Class<? extends Concept> c) {
      for (IFieldIndex idx : values(mapGet(fieldIndices, c))) if (idx instanceof IConceptCounter)
        return ((IConceptCounter) idx);
      for (IFieldIndex idx : values(mapGet(ciFieldIndices, c))) if (idx instanceof IConceptCounter)
        return ((IConceptCounter) idx);
      return null;
    }

    public <A extends Concept> int countConcepts(Class<A> c, Object... params) {
      int n = countConcepts_noParent(c, params);
      if (parent == null)
        return n;
      return n + parent.countConcepts(c, params);
    }

    public <A extends Concept> int countConcepts_noParent(Class<A> c, Object... params) {
      ping();
      if (empty(params)) {
        IConceptCounter counter = conceptCounterForClass(c);
        if (counter != null)
          return counter.countConcepts();
        return l(list_noParent(c));
      }
      int n = 0;
      for (A x : list_noParent(c)) if (checkConceptFields(x, params))
        ++n;
      return n;
    }

    public int countConcepts(String c, Object... params) {
      ping();
      if (empty(params))
        return l(list(c));
      int n = 0;
      for (Concept x : list(c)) if (checkConceptFields(x, params))
        ++n;
      return n;
    }

    public int countConcepts() {
      return l(concepts);
    }

    synchronized public List<IConceptIndex> clonedConceptIndices() {
      return cloneList(conceptIndices);
    }

    synchronized public void addConceptIndex(IConceptIndex index) {
      if (conceptIndices == null)
        conceptIndices = new ArrayList();
      conceptIndices.add(index);
    }

    synchronized public void removeConceptIndex(IConceptIndex index) {
      if (conceptIndices == null)
        return;
      conceptIndices.remove(index);
      if (empty(conceptIndices))
        conceptIndices = null;
    }

    synchronized public void addFieldIndex(Class<? extends Concept> c, String field, IFieldIndex index) {
      if (fieldIndices == null)
        fieldIndices = new HashMap();
      Map<String, IFieldIndex> map = fieldIndices.get(c);
      if (map == null)
        fieldIndices.put(c, map = new HashMap());
      map.put(field, index);
    }

    synchronized public void removeFieldIndex(Class<? extends Concept> c, String field, IFieldIndex index) {
      Map<String, IFieldIndex> map = mapGet(fieldIndices, c);
      mapRemove(map, field);
    }

    synchronized public IFieldIndex getFieldIndex(Class<? extends Concept> c, String field) {
      if (fieldIndices == null)
        return null;
      Map<String, IFieldIndex> map = fieldIndices.get(c);
      return map == null ? null : map.get(field);
    }

    synchronized public IFieldIndex getAnyIndexForClass(Class<? extends Concept> c) {
      return first(allIndicesForClass(c));
    }

    synchronized public Collection<IFieldIndex> allIndicesForClass(Class<? extends Concept> c) {
      return concatLists(values(fieldIndices == null ? null : fieldIndices.get(c)), values(ciFieldIndices == null ? null : ciFieldIndices.get(c)));
    }

    synchronized public void addCIFieldIndex(Class<? extends Concept> c, String field, IFieldIndex index) {
      if (ciFieldIndices == null)
        ciFieldIndices = new HashMap();
      Map<String, IFieldIndex> map = ciFieldIndices.get(c);
      if (map == null)
        ciFieldIndices.put(c, map = new HashMap());
      map.put(field, index);
    }

    synchronized public void removeCIFieldIndex(Class<? extends Concept> c, String field) {
      Map<String, IFieldIndex> map = mapGet(ciFieldIndices, c);
      mapRemove(map, field);
    }

    synchronized public IFieldIndex getCIFieldIndex(Class<? extends Concept> c, String field) {
      if (ciFieldIndices == null)
        return null;
      Map<String, IFieldIndex> map = ciFieldIndices.get(c);
      return map == null ? null : map.get(field);
    }

    public RC xnew(String name, Object... values) {
      return new RC(cnew(name, values));
    }

    public void xset(long id, String field, Object value) {
      xset(new RC(id), field, value);
    }

    public void xset(RC c, String field, Object value) {
      if (value instanceof RC)
        value = getConcept((RC) value);
      cset(getConcept(c), field, value);
    }

    public Object xget(long id, String field) {
      return xget(new RC(id), field);
    }

    public Object xget(RC c, String field) {
      return xgetPost(cget(getConcept(c), field));
    }

    public Object xgetPost(Object o) {
      o = deref(o);
      if (o instanceof Concept)
        return new RC((Concept) o);
      return o;
    }

    public void xdelete(long id) {
      xdelete(new RC(id));
    }

    public void xdelete(RC c) {
      getConcept(c).delete();
    }

    public void xdelete(List<RC> l) {
      for (RC c : l) xdelete(c);
    }

    public List<RC> xlist() {
      return map("toPassRef", allConcepts());
    }

    public List<RC> xlist(String className) {
      return map("toPassRef", conceptsOfType(className));
    }

    public boolean isTransient() {
      return eq(programID, "-");
    }

    public String xfullgrab() {
      if (noXFullGrab)
        throw fail("no xfullgrab (DB too large)");
      Lock __1 = lock();
      lock(__1);
      try {
        if (changes == changesWritten && !isTransient())
          return loadConceptsStructure(programID);
        return fullStructure();
      } finally {
        unlock(__1);
      }
    }

    public void xshutdown() {
      cleanKillVM();
    }

    public long xchangeCount() {
      return changes;
    }

    public int xcount() {
      return countConcepts();
    }

    public void register(Concept c) {
      ping();
      if (c._concepts == this)
        return;
      if (c._concepts != null)
        throw fail("Can't re-register");
      c.id = internalID();
      c.created = now();
      if (modifyOnCreate)
        c._setModified(c.created);
      register_phase2(c);
      vmBus_send("conceptCreated", c);
      fireChange(new ConceptCreate(c));
    }

    public void register_phase2(Concept c) {
      c._concepts = this;
      concepts.put((long) c.id, c);
      for (Concept.Ref r : c._refs()) r.index();
      c.change();
      c._onRegistered();
    }

    public void registerKeepingID(Concept c) {
      if (c._concepts == this)
        return;
      if (c._concepts != null)
        throw fail("Can't re-register");
      c._concepts = this;
      concepts.put((long) c.id, c);
      c.change();
    }

    public void conceptChanged(Concept c) {
      fireChange(new ConceptChange(c));
      if (conceptIndices != null)
        for (IConceptIndex index : clonedConceptIndices()) index.update(c);
    }

    public boolean hasUnsavedData() {
      return changes != changesWritten || savingConcepts;
    }

    synchronized public Object miscMapGet(Object key) {
      return mapGet(miscMap, key);
    }

    synchronized public Object miscMapPut(Object key, Object value) {
      if (miscMap == null)
        miscMap = new HashMap();
      return miscMap.put(key, value);
    }

    synchronized public void miscMapRemove(Object key) {
      mapRemove(miscMap, key);
    }

    synchronized public <A> A miscMapGetOrCreate(Object key, IF0<A> create) {
      if (containsKey(miscMap, key))
        return (A) miscMap.get(key);
      A value = create.get();
      miscMapPut(key, value);
      return value;
    }

    public void setParent(Concepts parent) {
      this.parent = parent;
    }

    public void fireChange(ConceptsChange change) {
      if (change == null)
        return;
      pcallFAll(onChange, change);
      fireLegacyChangeEvent();
    }

    final public void onChange(IVF1<ConceptsChange> l) {
      addChangeListener(l);
    }

    public void addChangeListener(IVF1<ConceptsChange> l) {
      syncAdd(onChange, l);
    }

    public void removeChangeListener(IVF1<ConceptsChange> l) {
      syncRemove(onChange, l);
    }

    public void addPreSave(Runnable r) {
      preSave = syncAddOrCreate(preSave, r);
    }

    public String toString() {
      return nConcepts(concepts) + " (" + conceptsDir() + ", hash: " + identityHashCode(this) + ")";
    }
  }

  public interface IConcept {

    public long _conceptID();

    public Concepts concepts();
  }

  static public class Concept extends DynamicObject implements IConcept, ChangeTriggerable {

    transient public Concepts _concepts;

    public long id;

    public long created, _modified;

    public List<Ref> backRefs;

    public Concept(String className) {
      super(className);
      _created();
    }

    public Concept() {
      if (!_loading()) {
        _created();
      }
    }

    public Concept(boolean unlisted) {
      if (!unlisted)
        _created();
    }

    public boolean includeZeroIDInToString() {
      return false;
    }

    public String toString() {
      String s = shortDynamicClassName(this);
      long id = this.id;
      if (id != 0 || includeZeroIDInToString())
        s += " " + id;
      return s;
    }

    static public boolean loading() {
      return _loading();
    }

    static public boolean _loading() {
      return dynamicObjectIsLoading();
    }

    public void _created() {
      if (!concepts_unlistedByDefault && !eq(concepts_unlisted.get(), true))
        db_mainConcepts().register(this);
    }

    public class TypedRef<A extends Concept, B> extends Ref<A> {

      public TypedRef() {
      }

      public Class<B> bType;

      public TypedRef(Class<B> bType) {
        this.bType = bType;
      }

      public TypedRef(Class<B> bType, B value) {
        this.bType = bType;
        set((A) value);
      }

      public TypedRef(B value) {
        set((A) value);
      }

      public boolean set(A a) {
        return super.set(checkValue(a));
      }

      public void check() {
        checkValue(get());
      }

      public <C> C checkValue(C a) {
        if (bType != null && a != null)
          assertIsInstance(a, bType);
        return a;
      }

      public B b() {
        return (B) value;
      }
    }

    public class Ref<A extends Concept> implements IRef<A> {

      public A value;

      public Ref() {
        if (!dynamicObjectIsLoading())
          registerRef();
      }

      public void registerRef() {
        vmBus_send("registeringConceptRef", this);
      }

      public Ref(A value) {
        this.value = value;
        registerRef();
        index();
      }

      public Concept concept() {
        return Concept.this;
      }

      public A get() {
        return value;
      }

      public boolean has() {
        return value != null;
      }

      public boolean set(A a) {
        if (a == value)
          return false;
        unindex();
        value = a;
        index();
        change();
        return true;
      }

      public void setIfEmpty(A a) {
        if (!has())
          set(a);
      }

      public void set(Ref<A> ref) {
        set(ref.get());
      }

      public void clear() {
        set((A) null);
      }

      public boolean validRef() {
        return value != null && _concepts != null && _concepts == value._concepts;
      }

      public void index() {
        if (validRef()) {
          value._addBackRef(this);
          change();
        }
      }

      public Ref<A> unindex() {
        if (validRef()) {
          value._removeBackRef(this);
          change();
        }
        return this;
      }

      public void unindexAndDrop() {
        unindex();
        _removeRef(this);
      }

      public void change() {
        Concept.this.change();
      }

      public String toString() {
        return str(value);
      }
    }

    public class RefL<A extends Concept> extends AbstractList<A> {

      public List<Ref<A>> l = new ArrayList();

      public RefL() {
      }

      public RefL(List<A> l) {
        replaceWithList(l);
      }

      public void clear() {
        while (!isEmpty()) main.removeLast(this);
      }

      public void replaceWithList(List<A> l) {
        clear();
        for (A a : unnullForIteration(l)) add(a);
      }

      public A set(int i, A o) {
        Ref<A> ref = syncGet(l, i);
        A prev = ref.get();
        ref.set(o);
        return prev;
      }

      public void add(int i, A o) {
        syncAdd(l, i, new Ref(o));
      }

      public A get(int i) {
        return syncGet(l, i).get();
      }

      public A remove(int i) {
        return syncRemove(l, i).get();
      }

      public int size() {
        return syncL(l);
      }

      public boolean contains(Object o) {
        if (o instanceof Concept)
          for (Ref<A> r : l) if (eq(r.get(), o))
            return true;
        return super.contains(o);
      }
    }

    public void delete() {
      for (Ref r : unnullForIteration(_refs())) r.unindex();
      for (Ref r : cloneList(backRefs)) r.set((Concept) null);
      backRefs = null;
      var _concepts = this._concepts;
      if (_concepts != null) {
        _concepts.concepts.remove(id);
        _concepts.fireChange(new ConceptDelete(id, this));
        if (_concepts.conceptIndices != null)
          for (IConceptIndex index : _concepts.conceptIndices) index.remove(this);
        this._concepts = null;
      }
      id = 0;
    }

    public BaseXRef export() {
      return new BaseXRef(_concepts.progID(), id);
    }

    final public void _change() {
      change();
    }

    public void change() {
      _setModified(now());
      _change_withoutUpdatingModifiedField();
    }

    public void _setModified(long modified) {
      _modified = modified;
    }

    final public void _change_withoutUpdatingModifiedField() {
      _onChange();
      if (_concepts != null)
        _concepts.conceptChanged(this);
    }

    public void _onChange() {
    }

    public String _programID() {
      return _concepts == null ? getDBProgramID() : _concepts.progID();
    }

    public void _addBackRef(Concept.Ref ref) {
      backRefs = addDyn_quickSync(backRefs, ref);
      _backRefsModified();
    }

    public void _backRefsModified() {
      if (_concepts != null && _concepts.modifyOnBackRef)
        change();
    }

    public void _removeBackRef(Concept.Ref ref) {
      backRefs = removeDyn_quickSync(backRefs, ref);
      _backRefsModified();
    }

    public void _removeRef(Concept.Ref ref) {
    }

    public int _backRefCount() {
      return syncL(backRefs);
    }

    final public void setField(String field, Object value) {
      _setField(field, value);
    }

    public void _setField(String field, Object value) {
      cset(this, field, value);
    }

    public boolean setField_trueIfChanged(String field, Object value) {
      return cset(this, field, value) != 0;
    }

    public <A> A setFieldAndReturn(String field, A value) {
      setField(field, value);
      return value;
    }

    final public void setFields(Object... values) {
      _setFields(values);
    }

    public void _setFields(Object... values) {
      cset(this, values);
    }

    public Concepts concepts() {
      return _concepts;
    }

    public boolean isDeleted() {
      return id == 0;
    }

    public void _doneLoading() {
    }

    public void _doneLoading2() {
      Map<String, FieldMigration> map = _fieldMigrations();
      if (map != null)
        for (Map.Entry<? extends String, ? extends FieldMigration> __0 : _entrySet(map)) {
          String oldField = __0.getKey();
          FieldMigration m = __0.getValue();
          crenameField_noOverwrite(this, oldField, m.newField);
        }
    }

    static public class FieldMigration implements IFieldsToList {

      public String newField;

      public FieldMigration() {
      }

      public FieldMigration(String newField) {
        this.newField = newField;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + newField + ")";
      }

      public boolean equals(Object o) {
        if (!(o instanceof FieldMigration))
          return false;
        FieldMigration __6 = (FieldMigration) o;
        return eq(newField, __6.newField);
      }

      public int hashCode() {
        int h = 558692372;
        h = boostHashCombine(h, _hashCode(newField));
        return h;
      }

      public Object[] _fieldsToList() {
        return new Object[] { newField };
      }
    }

    public Map<String, FieldMigration> _fieldMigrations() {
      return null;
    }

    public Collection<Ref> _refs() {
      return scanConceptForRefs(this);
    }

    public Concepts _concepts() {
      return _concepts;
    }

    public boolean _conceptsDefunct() {
      return _concepts != null && _concepts.defunct;
    }

    public boolean _conceptsDefunctOrUnregistered() {
      return _concepts == null || _concepts.defunct;
    }

    public void _onRegistered() {
    }

    public <A> boolean addAndChange(Collection<A> cl, A a) {
      if (cl == null || !cl.add(a))
        return false;
      change();
      return true;
    }

    public <A> void clearAndChange(Collection<A> cl) {
      if (cl == null)
        return;
      cl.clear();
      change();
    }

    public File conceptsDir() {
      var concepts = concepts();
      return concepts == null ? null : concepts.conceptsDir();
    }

    public File fileInConceptsDir(String name) {
      var dir = conceptsDir();
      return dir == null ? null : newFile(dir, name);
    }

    public long _conceptID() {
      return id;
    }
  }

  static public class RC {

    transient public Object owner;

    public String id;

    public RC() {
    }

    public RC(long id) {
      this.id = str(id);
    }

    public RC(Object owner, long id) {
      this.id = str(id);
      this.owner = owner;
    }

    public RC(Concept c) {
      this(c.id);
    }

    public long longID() {
      return parseLong(id);
    }

    public String toString() {
      return id;
    }

    transient public RemoteDB db;

    public String getString(String field) {
      return db.xS(this, field);
    }

    public Object get(String field) {
      return db.xget(this, field);
    }

    public void set(String field, Object value) {
      db.xset(this, field, value);
    }
  }

  static public class BaseXRef {

    public String programID;

    public long id;

    public BaseXRef() {
    }

    public BaseXRef(String programID, long id) {
      this.id = id;
      this.programID = programID;
    }

    public boolean equals(Object o) {
      if (!(o instanceof BaseXRef))
        return false;
      BaseXRef r = (BaseXRef) o;
      return eq(programID, r.programID) && eq(id, r.id);
    }

    public int hashCode() {
      return programID.hashCode() + (int) id;
    }
  }

  static public class XRef extends Concept {

    public BaseXRef ref;

    public XRef() {
    }

    public XRef(BaseXRef ref) {
      this.ref = ref;
      _doneLoading2();
    }

    public void _doneLoading2() {
      getIndex().put(ref, this);
    }

    public HashMap<BaseXRef, XRef> getIndex() {
      return getXRefIndex(_concepts);
    }
  }

  static synchronized public HashMap<BaseXRef, XRef> getXRefIndex(Concepts concepts) {
    HashMap cache = (HashMap) concepts.perClassData().get(XRef.class);
    if (cache == null)
      concepts.perClassData.put(XRef.class, cache = new HashMap());
    return cache;
  }

  static public XRef lookupOrCreateXRef(BaseXRef ref) {
    XRef xref = getXRefIndex(db_mainConcepts()).get(ref);
    if (xref == null)
      xref = new XRef(ref);
    return xref;
  }

  static public void loadAndAutoSaveConcepts() {
    db_mainConcepts().persist();
  }

  static public void loadAndAutoSaveConcepts(int interval) {
    db_mainConcepts().persist(interval);
  }

  static public RC toPassRef(Concept c) {
    return new RC(c);
  }

  static public void concepts_setUnlistedByDefault(boolean b) {
    concepts_unlistedByDefault = b;
  }

  static public interface IContentsIndexedList2<A> extends List<A> {

    public TreeSet<HasIndex> indicesOf_treeSetOfHasIndex(A o);
  }

  static public interface Producer<A> {

    public A next();
  }

  static public class Lowest<A> implements IBest<A> {

    public A best;

    public double score;

    transient public Object onChange;

    synchronized public boolean isNewBest(double score) {
      return best == null || score < this.score;
    }

    synchronized public double bestScore() {
      return best == null ? Double.NaN : score;
    }

    public double score() {
      return bestScore();
    }

    synchronized public float floatScore() {
      return best == null ? Float.NaN : (float) score;
    }

    synchronized public float floatScoreOr(float defaultValue) {
      return best == null ? defaultValue : (float) score;
    }

    public boolean put(A a, double score) {
      boolean change = false;
      synchronized (this) {
        if (a != null && isNewBest(score)) {
          best = a;
          this.score = score;
          change = true;
        }
      }
      if (change)
        pcallF(onChange);
      return change;
    }

    synchronized public void clear() {
      best = null;
      score = 0;
    }

    synchronized public A get() {
      return best;
    }

    synchronized public boolean has() {
      return best != null;
    }

    synchronized public Pair<A, Double> pair() {
      return best == null ? null : new Pair(best, bestScore());
    }

    public String toString() {
      return "Score " + formatDouble_significant2(score, 4) + ": " + best;
    }
  }

  static public class ReverseChain<A> implements Iterable<A> {

    public A element;

    public ReverseChain<A> prev;

    public int size;

    public ReverseChain() {
    }

    public ReverseChain(ReverseChain<A> prev, A element) {
      this.element = element;
      this.prev = prev;
      if (prev == null)
        size = 1;
      else {
        prev.check();
        size = prev.size + 1;
      }
    }

    public void check() {
      if (size < 1)
        throw fail("You called the ReverseChain default constructor. Don't do that");
    }

    public String toString() {
      return str(toList());
    }

    public ArrayList<A> toList() {
      check();
      ArrayList<A> l = emptyList(size);
      for (int i = 0; i < size; i++) l.add(null);
      int i = size;
      ReverseChain<A> c = this;
      while (c != null) {
        l.set(--i, c.element);
        c = c.prev;
      }
      return l;
    }

    public Iterator<A> iterator() {
      return toList().iterator();
    }
  }

  public interface TransientObject {
  }

  static public class SynchronizedSortedMap<K, V> extends SynchronizedMap<K, V> implements SortedMap<K, V> {

    public SynchronizedSortedMap() {
    }

    public SortedMap<K, V> innerMap() {
      return (SortedMap) m;
    }

    public SynchronizedSortedMap(SortedMap<K, V> m) {
      super(m);
    }

    public SynchronizedSortedMap(SortedMap<K, V> m, Object mutex) {
      super(m, mutex);
    }

    public Comparator<? super K> comparator() {
      synchronized (mutex) {
        return innerMap().comparator();
      }
    }

    public SortedMap<K, V> subMap(K fromKey, K toKey) {
      synchronized (mutex) {
        return new SynchronizedSortedMap<>(innerMap().subMap(fromKey, toKey), mutex);
      }
    }

    public SortedMap<K, V> headMap(K toKey) {
      synchronized (mutex) {
        return new SynchronizedSortedMap<>(innerMap().headMap(toKey), mutex);
      }
    }

    public SortedMap<K, V> tailMap(K fromKey) {
      synchronized (mutex) {
        return new SynchronizedSortedMap<>(innerMap().tailMap(fromKey), mutex);
      }
    }

    public K firstKey() {
      synchronized (mutex) {
        return innerMap().firstKey();
      }
    }

    public K lastKey() {
      synchronized (mutex) {
        return innerMap().lastKey();
      }
    }
  }

  static public interface IIntPred {

    public boolean get(int a);
  }

  static public class HasIndex implements Comparable<HasIndex> {

    public int idx;

    public HasIndex() {
    }

    public HasIndex(int idx) {
      this.idx = idx;
    }

    public int compareTo(HasIndex h) {
      return idx - h.idx;
    }

    final public int get() {
      return idx;
    }
  }

  final static public class TokenIndexedList3 extends RandomAccessAbstractList<String> implements IContentsIndexedList<String>, IContentsIndexedList2<String> {

    final public HashMap<String, TreeSet<Token>> index = new HashMap();

    final public ArrayList<Token> list = new ArrayList();

    final static public class Token extends HasIndex {

      public String s;

      public String toString() {
        return "Token " + quote(s) + "@" + idx;
      }
    }

    public TokenIndexedList3() {
    }

    public TokenIndexedList3(Collection<String> l) {
      addAll(l);
    }

    public String get(int i) {
      return list.get(i).s;
    }

    public int size() {
      return list.size();
    }

    public String set(int i, String s) {
      Token t = list.get(i);
      String old = t.s;
      if (eq(old, s))
        return old;
      removeFromIdx(t);
      t.s = s;
      addToIdx(t);
      return old;
    }

    public boolean add(String s) {
      ++modCount;
      Token t = new Token();
      t.s = s;
      t.idx = size();
      list.add(t);
      addToIdx(t);
      return true;
    }

    public void add(int i, String s) {
      ++modCount;
      Token t = new Token();
      t.s = s;
      t.idx = i;
      list.add(i, t);
      reorder(i + 1);
      addToIdx(t);
    }

    public boolean addAll(int i, Collection<? extends String> l) {
      int n = l.size();
      if (n == 0)
        return false;
      ++modCount;
      List<Token> l2 = emptyList(n);
      int j = i;
      for (String s : l) {
        Token t = new Token();
        t.s = s;
        t.idx = j++;
        l2.add(t);
      }
      list.addAll(i, l2);
      reorder(i + n);
      for (Token t : l2) addToIdx(t);
      return true;
    }

    public String remove(int i) {
      ++modCount;
      Token t = list.get(i);
      removeFromIdx(t);
      list.remove(i);
      reorder(i);
      return t.s;
    }

    public void reorder(int fromIdx) {
      int n = size();
      for (int i = fromIdx; i < n; i++) list.get(i).idx = i;
    }

    public void removeFromIdx(Token t) {
      TreeSet<Token> idx = index.get(t.s);
      idx.remove(t);
      if (idx.isEmpty())
        index.remove(t.s);
    }

    public void addToIdx(Token t) {
      TreeSet<Token> idx = index.get(t.s);
      if (idx == null)
        index.put(t.s, idx = new TreeSet());
      idx.add(t);
    }

    @Override
    public int indexOf(Object s) {
      TreeSet<Token> l = index.get(s);
      return l == null ? -1 : first(l).idx;
    }

    @Override
    public int lastIndexOf(Object s) {
      TreeSet<Token> l = index.get(s);
      return l == null ? -1 : last(l).idx;
    }

    @Override
    public boolean contains(Object s) {
      return index.containsKey(s);
    }

    public void clear() {
      ++modCount;
      index.clear();
      list.clear();
    }

    public void removeRange(int fromIndex, int toIndex) {
      if (fromIndex == toIndex)
        return;
      ++modCount;
      for (int i = fromIndex; i < toIndex; i++) removeFromIdx(list.get(i));
      list.subList(fromIndex, toIndex).clear();
      reorder(fromIndex);
    }

    public int[] indicesOf(String o) {
      TreeSet<Token> idx = index.get(o);
      if (idx == null)
        return emptyIntArray();
      int[] a = new int[idx.size()];
      int i = 0;
      for (Token t : idx) a[i++] = t.idx;
      return a;
    }

    public TreeSet<HasIndex> indicesOf_treeSetOfHasIndex(String o) {
      return (TreeSet) index.get(o);
    }
  }

  static public class FileBasedLock implements AutoCloseable {

    public File lockFile;

    public double timeout = 60.0;

    public boolean verbose = false;

    public boolean haveLock = false;

    public java.util.Timer touchTimer;

    final public FileBasedLock setContentsForLockFile(String contentsForLockFile) {
      return contentsForLockFile(contentsForLockFile);
    }

    public FileBasedLock contentsForLockFile(String contentsForLockFile) {
      this.contentsForLockFile = contentsForLockFile;
      return this;
    }

    final public String getContentsForLockFile() {
      return contentsForLockFile();
    }

    public String contentsForLockFile() {
      return contentsForLockFile;
    }

    public String contentsForLockFile;

    public FileBasedLock() {
    }

    public FileBasedLock(File lockFile) {
      this.lockFile = lockFile;
    }

    public FileBasedLock(File lockFile, double timeout) {
      this.timeout = timeout;
      this.lockFile = lockFile;
    }

    synchronized public boolean tryToLock() {
      if (haveLock)
        return true;
      if (fileExists(lockFile)) {
        double age = fileAgeInSeconds(lockFile);
        double remaining = timeout - age;
        print("Lock file age: " + lockFile + ": " + iround(age) + " s" + (remaining <= 0 ? " - old, deleting" : " - please start again in " + nSeconds(iceil(remaining))));
        if (remaining <= 0) {
          print("Deleting old lock file (program crashed?): " + lockFile + " (age: " + iround(age) + " seconds)");
          deleteFile(lockFile);
        }
      }
      try {
        mkdirsForFile(lockFile);
        java.nio.file.Files.createFile(toPath(lockFile));
        print("Created lock file: " + lockFile);
        if (nempty(contentsForLockFile))
          writeContents();
        acquired();
        return true;
      } catch (Throwable e) {
        printExceptionShort("Can't lock", e);
        return false;
      }
    }

    public void writeContents() {
      saveTextFileWithoutTemp(lockFile, unnull(contentsForLockFile));
    }

    private void acquired() {
      haveLock = true;
      startTouchTimer();
    }

    public void forceLock() {
      try {
        print("Force-locking " + lockFile);
        writeContents();
        acquired();
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public String lockError() {
      return "Couldn't aquire lock file: " + lockFile;
    }

    public void lockOrFail() {
      if (!tryToLock())
        throw fail(lockError());
    }

    synchronized public void startTouchTimer() {
      if (touchTimer != null)
        return;
      double interval = timeout / 2;
      touchTimer = doEvery(interval, new Runnable() {

        public void run() {
          try {
            doTouch();
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "doTouch();";
        }
      });
      if (verbose)
        print("Touch timer started for " + lockFile + " (" + interval + "s)");
    }

    synchronized public void doTouch() {
      try {
        if (haveLock) {
          if (verbose)
            print("Touching lock file: " + lockFile);
          touchFile(lockFile);
        }
      } catch (Throwable __e) {
        pcallFail(__e);
      }
    }

    public synchronized void close() {
      try {
        {
          cleanUp(touchTimer);
          touchTimer = null;
        }
        if (haveLock) {
          haveLock = false;
          if (verbose)
            print("Deleting lock file: " + lockFile);
          deleteFile(lockFile);
        }
      } catch (Throwable __e) {
        pcallFail(__e);
      }
    }

    synchronized public void _simulateCrash() {
      {
        cleanUp(touchTimer);
        touchTimer = null;
      }
    }

    public void deleteOnExit() {
      if (haveLock)
        lockFile.deleteOnExit();
    }

    public String actualContents() {
      return loadTextFile(lockFile);
    }

    public boolean hasExpectedContents() {
      return eq(unnull(contentsForLockFile), actualContents());
    }
  }

  static public interface IConceptCounter {

    public Class<? extends Concept> conceptClass();

    public int countConcepts();

    public Collection<Concept> allConcepts();
  }

  static public interface IRef<A> extends IF0<A> {

    public default void replaceValue(A oldValue, A newValue) {
    }
  }

  public interface IBest<A> {

    public boolean put(A a, double score);
  }

  abstract static public class CompactAbstractMap<K, V> implements Map<K, V> {

    public int size() {
      return entrySet().size();
    }

    public boolean isEmpty() {
      return size() == 0;
    }

    public boolean containsValue(Object value) {
      Iterator<Entry<K, V>> i = entrySet().iterator();
      if (value == null) {
        while (i.hasNext()) {
          Entry<K, V> e = i.next();
          if (e.getValue() == null)
            return true;
        }
      } else {
        while (i.hasNext()) {
          Entry<K, V> e = i.next();
          if (value.equals(e.getValue()))
            return true;
        }
      }
      return false;
    }

    public boolean containsKey(Object key) {
      Iterator<Entry<K, V>> i = entrySet().iterator();
      if (key == null) {
        while (i.hasNext()) {
          Entry<K, V> e = i.next();
          if (e.getKey() == null)
            return true;
        }
      } else {
        while (i.hasNext()) {
          Entry<K, V> e = i.next();
          if (key.equals(e.getKey()))
            return true;
        }
      }
      return false;
    }

    public V get(Object key) {
      Iterator<Entry<K, V>> i = entrySet().iterator();
      if (key == null) {
        while (i.hasNext()) {
          Entry<K, V> e = i.next();
          if (e.getKey() == null)
            return e.getValue();
        }
      } else {
        while (i.hasNext()) {
          Entry<K, V> e = i.next();
          if (key.equals(e.getKey()))
            return e.getValue();
        }
      }
      return null;
    }

    public V put(K key, V value) {
      throw new UnsupportedOperationException();
    }

    public V remove(Object key) {
      Iterator<Entry<K, V>> i = entrySet().iterator();
      Entry<K, V> correctEntry = null;
      if (key == null) {
        while (correctEntry == null && i.hasNext()) {
          Entry<K, V> e = i.next();
          if (e.getKey() == null)
            correctEntry = e;
        }
      } else {
        while (correctEntry == null && i.hasNext()) {
          Entry<K, V> e = i.next();
          if (key.equals(e.getKey()))
            correctEntry = e;
        }
      }
      V oldValue = null;
      if (correctEntry != null) {
        oldValue = correctEntry.getValue();
        i.remove();
      }
      return oldValue;
    }

    public void putAll(Map<? extends K, ? extends V> m) {
      for (Entry<? extends K, ? extends V> e : m.entrySet()) put(e.getKey(), e.getValue());
    }

    public void clear() {
      entrySet().clear();
    }

    public Set<K> keySet() {
      return new AbstractSet<K>() {

        public Iterator<K> iterator() {
          return new Iterator<K>() {

            public Iterator<Entry<K, V>> i = entrySet().iterator();

            public boolean hasNext() {
              return i.hasNext();
            }

            public K next() {
              return i.next().getKey();
            }

            public void remove() {
              i.remove();
            }
          };
        }

        public int size() {
          return CompactAbstractMap.this.size();
        }

        public boolean isEmpty() {
          return CompactAbstractMap.this.isEmpty();
        }

        public void clear() {
          CompactAbstractMap.this.clear();
        }

        public boolean contains(Object k) {
          return CompactAbstractMap.this.containsKey(k);
        }
      };
    }

    public Collection<V> values() {
      return new AbstractCollection<V>() {

        public Iterator<V> iterator() {
          return new Iterator<V>() {

            public Iterator<Entry<K, V>> i = entrySet().iterator();

            public boolean hasNext() {
              return i.hasNext();
            }

            public V next() {
              return i.next().getValue();
            }

            public void remove() {
              i.remove();
            }
          };
        }

        public int size() {
          return CompactAbstractMap.this.size();
        }

        public boolean isEmpty() {
          return CompactAbstractMap.this.isEmpty();
        }

        public void clear() {
          CompactAbstractMap.this.clear();
        }

        public boolean contains(Object v) {
          return CompactAbstractMap.this.containsValue(v);
        }
      };
    }

    public abstract Set<Entry<K, V>> entrySet();

    public boolean equals(Object o) {
      if (o == this)
        return true;
      if (!(o instanceof Map))
        return false;
      Map<?, ?> m = (Map<?, ?>) o;
      if (m.size() != size())
        return false;
      try {
        for (Entry<K, V> e : entrySet()) {
          K key = e.getKey();
          V value = e.getValue();
          if (value == null) {
            if (!(m.get(key) == null && m.containsKey(key)))
              return false;
          } else {
            if (!value.equals(m.get(key)))
              return false;
          }
        }
      } catch (ClassCastException unused) {
        return false;
      } catch (NullPointerException unused) {
        return false;
      }
      return true;
    }

    public int hashCode() {
      int h = 0;
      for (Entry<K, V> entry : entrySet()) h += entry.hashCode();
      return h;
    }

    public String toString() {
      Iterator<Entry<K, V>> i = entrySet().iterator();
      if (!i.hasNext())
        return "{}";
      StringBuilder sb = new StringBuilder();
      sb.append('{');
      for (; ; ) {
        Entry<K, V> e = i.next();
        K key = e.getKey();
        V value = e.getValue();
        sb.append(key == this ? "(this Map)" : key);
        sb.append('=');
        sb.append(value == this ? "(this Map)" : value);
        if (!i.hasNext())
          return sb.append('}').toString();
        sb.append(',').append(' ');
      }
    }

    public Object clone() throws CloneNotSupportedException {
      CompactAbstractMap<?, ?> result = (CompactAbstractMap<?, ?>) super.clone();
      return result;
    }

    public static class SimpleEntry<K, V> implements Entry<K, V>, java.io.Serializable {

      @java.io.Serial
      static final public long serialVersionUID = -8499721149061103585L;

      @SuppressWarnings("serial")
      final public K key;

      @SuppressWarnings("serial")
      public V value;

      public SimpleEntry(K key, V value) {
        this.key = key;
        this.value = value;
      }

      public SimpleEntry(Entry<? extends K, ? extends V> entry) {
        this.key = entry.getKey();
        this.value = entry.getValue();
      }

      public K getKey() {
        return key;
      }

      public V getValue() {
        return value;
      }

      public V setValue(V value) {
        V oldValue = this.value;
        this.value = value;
        return oldValue;
      }

      public boolean equals(Object o) {
        if (!(o instanceof Map.Entry))
          return false;
        Entry<?, ?> e = (Entry<?, ?>) o;
        return eq(key, e.getKey()) && eq(value, e.getValue());
      }

      public int hashCode() {
        return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());
      }

      public String toString() {
        return key + "=" + value;
      }
    }

    public static class SimpleImmutableEntry<K, V> implements Entry<K, V>, java.io.Serializable {

      @java.io.Serial
      static final public long serialVersionUID = 7138329143949025153L;

      @SuppressWarnings("serial")
      final public K key;

      @SuppressWarnings("serial")
      final public V value;

      public SimpleImmutableEntry(K key, V value) {
        this.key = key;
        this.value = value;
      }

      public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
        this.key = entry.getKey();
        this.value = entry.getValue();
      }

      public K getKey() {
        return key;
      }

      public V getValue() {
        return value;
      }

      public V setValue(V value) {
        throw new UnsupportedOperationException();
      }

      public boolean equals(Object o) {
        if (!(o instanceof Map.Entry))
          return false;
        Entry<?, ?> e = (Entry<?, ?>) o;
        return eq(key, e.getKey()) && eq(value, e.getValue());
      }

      public int hashCode() {
        return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());
      }

      public String toString() {
        return key + "=" + value;
      }
    }
  }

  static public class MultiSet<A> implements IMultiSet<A> {

    public Map<A, Integer> map = new HashMap();

    public int size;

    public MultiSet(boolean useTreeMap) {
      if (useTreeMap)
        map = new TreeMap();
    }

    public MultiSet(TreeMap map) {
      this.map = map;
    }

    public MultiSet() {
    }

    public MultiSet(Iterable<A> c) {
      addAll(c);
    }

    public MultiSet(MultiSet<A> ms) {
      synchronized (ms) {
        for (A a : ms.keySet()) add(a, ms.get(a));
      }
    }

    public synchronized int add(A key) {
      return add(key, 1);
    }

    synchronized public void addAll(Iterable<A> c) {
      if (c != null)
        for (A a : c) add(a);
    }

    synchronized public void addAll(MultiSet<A> ms) {
      for (A a : ms.keySet()) add(a, ms.get(a));
    }

    synchronized public int add(A key, int count) {
      if (count <= 0)
        return 0;
      size += count;
      Integer i = map.get(key);
      map.put(key, i != null ? (count += i) : count);
      return count;
    }

    synchronized public void put(A key, int count) {
      int oldCount = get(key);
      if (count == oldCount)
        return;
      size += count - oldCount;
      if (count != 0)
        map.put(key, count);
      else
        map.remove(key);
    }

    public synchronized int get(A key) {
      Integer i = map.get(key);
      return i != null ? i : 0;
    }

    synchronized public boolean contains(A key) {
      return map.containsKey(key);
    }

    synchronized public void remove(A key) {
      Integer i = map.get(key);
      if (i != null) {
        --size;
        if (i > 1)
          map.put(key, i - 1);
        else
          map.remove(key);
      }
    }

    synchronized public List<A> topTen() {
      return getTopTen();
    }

    synchronized public List<A> getTopTen() {
      return getTopTen(10);
    }

    synchronized public List<A> getTopTen(int maxSize) {
      List<A> list = getSortedListDescending();
      return list.size() > maxSize ? list.subList(0, maxSize) : list;
    }

    synchronized public List<A> highestFirst() {
      return getSortedListDescending();
    }

    synchronized public List<A> lowestFirst() {
      return reversedList(getSortedListDescending());
    }

    synchronized public List<A> getSortedListDescending() {
      List<A> list = new ArrayList<A>(map.keySet());
      Collections.sort(list, new Comparator<A>() {

        public int compare(A a, A b) {
          return map.get(b).compareTo(map.get(a));
        }
      });
      return list;
    }

    synchronized public int getNumberOfUniqueElements() {
      return map.size();
    }

    synchronized public int uniqueSize() {
      return map.size();
    }

    synchronized public Set<A> asSet() {
      return map.keySet();
    }

    synchronized public NavigableSet<A> navigableSet() {
      return navigableKeys((NavigableMap) map);
    }

    public synchronized Set<A> keySet() {
      return map.keySet();
    }

    final public A mostPopular() {
      return getMostPopularEntry();
    }

    synchronized public A getMostPopularEntry() {
      int max = 0;
      A a = null;
      for (Map.Entry<A, Integer> entry : map.entrySet()) {
        if (entry.getValue() > max) {
          max = entry.getValue();
          a = entry.getKey();
        }
      }
      return a;
    }

    synchronized public void removeAll(A key) {
      size -= get(key);
      map.remove(key);
    }

    public synchronized int size() {
      return size;
    }

    synchronized public MultiSet<A> mergeWith(MultiSet<A> set) {
      MultiSet<A> result = new MultiSet<A>();
      for (A a : set.asSet()) {
        result.add(a, set.get(a));
      }
      return result;
    }

    public synchronized boolean isEmpty() {
      return map.isEmpty();
    }

    synchronized public String toString() {
      return str(map);
    }

    synchronized public void clear() {
      map.clear();
      size = 0;
    }

    final public Map<A, Integer> toMap() {
      return asMap();
    }

    synchronized public Map<A, Integer> asMap() {
      return cloneMap(map);
    }
  }

  abstract static public class RandomAccessAbstractList<A> extends AbstractList<A> implements RandomAccess {
  }

  static public class RemoteDB implements AutoCloseable {

    public DialogIO db;

    public String name;

    public RemoteDB(String s) {
      this(s, false);
    }

    public RemoteDB(String s, boolean autoStart) {
      name = s;
      if (isSnippetID(s))
        name = dbBotName(s);
      db = findBot(name);
      if (db == null)
        if (autoStart) {
          nohupJavax(fsI(s));
          waitForBotStartUp(name);
          assertNotNull("Weird problem", db = findBot(s));
        } else
          throw fail("DB " + s + " not running");
    }

    public boolean functional() {
      return db != null;
    }

    public List<RC> list() {
      return adopt((List<RC>) rpc(db, "xlist"));
    }

    public List<RC> list(String className) {
      return adopt((List<RC>) rpc(db, "xlist", className));
    }

    public List<RC> xlist() {
      return list();
    }

    public List<RC> xlist(String className) {
      return list(className);
    }

    public List<RC> adopt(List<RC> l) {
      if (l != null)
        for (RC rc : l) adopt(rc);
      return l;
    }

    public RC adopt(RC rc) {
      if (rc != null)
        rc.db = this;
      return rc;
    }

    public Object adopt(Object o) {
      if (o instanceof RC)
        return adopt((RC) o);
      return o;
    }

    public String xclass(RC o) {
      return (String) rpc(db, "xclass", o);
    }

    public Object xget(RC o, String field) {
      return adopt(rpc(db, "xget", o, field));
    }

    public String xS(RC o, String field) {
      return (String) xget(o, field);
    }

    public RC xgetref(RC o, String field) {
      return adopt((RC) xget(o, field));
    }

    public void xset(RC o, String field, Object value) {
      rpc(db, "xset", o, field, value);
    }

    public RC uniq(String className) {
      RC ref = first(list(className));
      if (ref == null)
        ref = xnew(className);
      return ref;
    }

    public RC xuniq(String className) {
      return uniq(className);
    }

    public RC xnew(String className, Object... values) {
      return adopt((RC) rpc(db, "xnew", className, values));
    }

    public void xdelete(RC o) {
      rpc(db, "xdelete", o);
    }

    public void xdelete(List<RC> l) {
      rpc(db, "xdelete", l);
    }

    public void close() {
      _close(db);
    }

    public String fullgrab() {
      return (String) rpc(db, "xfullgrab");
    }

    public String xfullgrab() {
      return fullgrab();
    }

    public void xshutdown() {
      rpc(db, "xshutdown");
    }

    public long xchangeCount() {
      return (long) rpc(db, "xchangeCount");
    }

    public int xcount() {
      return (int) rpc(db, "xcount");
    }

    public void reconnect() {
      close();
      db = findBot(name);
    }

    public RC rc(long id) {
      return new RC(this, id);
    }
  }

  static public class SynchronizedNavigableSet<E> extends SynchronizedSortedSet<E> implements NavigableSet<E> {

    public SynchronizedNavigableSet() {
    }

    @java.io.Serial
    static final public long serialVersionUID = -5505529816273629798L;

    @SuppressWarnings("serial")
    public NavigableSet<E> ns;

    public SynchronizedNavigableSet(NavigableSet<E> s) {
      super(s);
      ns = s;
    }

    public SynchronizedNavigableSet(NavigableSet<E> s, Object mutex) {
      super(s, mutex);
      ns = s;
    }

    public E lower(E e) {
      synchronized (mutex) {
        return ns.lower(e);
      }
    }

    public E floor(E e) {
      synchronized (mutex) {
        return ns.floor(e);
      }
    }

    public E ceiling(E e) {
      synchronized (mutex) {
        return ns.ceiling(e);
      }
    }

    public E higher(E e) {
      synchronized (mutex) {
        return ns.higher(e);
      }
    }

    public E pollFirst() {
      synchronized (mutex) {
        return ns.pollFirst();
      }
    }

    public E pollLast() {
      synchronized (mutex) {
        return ns.pollLast();
      }
    }

    public NavigableSet<E> descendingSet() {
      synchronized (mutex) {
        return new SynchronizedNavigableSet<>(ns.descendingSet(), mutex);
      }
    }

    public Iterator<E> descendingIterator() {
      synchronized (mutex) {
        return descendingSet().iterator();
      }
    }

    public NavigableSet<E> subSet(E fromElement, E toElement) {
      synchronized (mutex) {
        return new SynchronizedNavigableSet<>(ns.subSet(fromElement, true, toElement, false), mutex);
      }
    }

    public NavigableSet<E> headSet(E toElement) {
      synchronized (mutex) {
        return new SynchronizedNavigableSet<>(ns.headSet(toElement, false), mutex);
      }
    }

    public NavigableSet<E> tailSet(E fromElement) {
      synchronized (mutex) {
        return new SynchronizedNavigableSet<>(ns.tailSet(fromElement, true), mutex);
      }
    }

    public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
      synchronized (mutex) {
        return new SynchronizedNavigableSet<>(ns.subSet(fromElement, fromInclusive, toElement, toInclusive), mutex);
      }
    }

    public NavigableSet<E> headSet(E toElement, boolean inclusive) {
      synchronized (mutex) {
        return new SynchronizedNavigableSet<>(ns.headSet(toElement, inclusive), mutex);
      }
    }

    public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
      synchronized (mutex) {
        return new SynchronizedNavigableSet<>(ns.tailSet(fromElement, inclusive), mutex);
      }
    }
  }

  public interface ChangeTriggerable {

    public void change();
  }

  static public interface IMultiSet<A> extends IntSize {

    public int add(A key);

    public int get(A key);

    public Set<A> keySet();
  }

  static abstract public class DialogIO implements AutoCloseable {

    public String line;

    public boolean eos, loud, noClose;

    public Lock lock = lock();

    abstract public String readLineImpl();

    abstract public boolean isStillConnected();

    abstract public void sendLine(String line);

    abstract public boolean isLocalConnection();

    abstract public Socket getSocket();

    public int getPort() {
      Socket s = getSocket();
      return s == null ? 0 : s.getPort();
    }

    public boolean helloRead = false;

    public int shortenOutputTo = 500;

    public String readLineNoBlock() {
      String l = line;
      line = null;
      return l;
    }

    public boolean waitForLine() {
      try {
        ping();
        if (line != null)
          return true;
        line = readLineImpl();
        if (line == null)
          eos = true;
        return line != null;
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public String readLine() {
      waitForLine();
      helloRead = true;
      return readLineNoBlock();
    }

    public String ask(String s, Object... args) {
      if (loud)
        return askLoudly(s, args);
      if (!helloRead)
        readLine();
      if (args.length != 0)
        s = format3(s, args);
      sendLine(s);
      return readLine();
    }

    public String askLoudly(String s, Object... args) {
      if (!helloRead)
        readLine();
      if (args.length != 0)
        s = format3(s, args);
      print("> " + shorten(s, shortenOutputTo));
      sendLine(s);
      String answer = readLine();
      print("< " + shorten(answer, shortenOutputTo));
      return answer;
    }

    public void pushback(String l) {
      if (line != null)
        throw fail();
      line = l;
      helloRead = false;
    }
  }

  static abstract public class DialogHandler implements IDialogHandler {
  }

  public interface IDialogHandler {

    public void run(DialogIO io);
  }

  static public class SynchronizedSortedSet<E> extends SynchronizedSet<E> implements SortedSet<E> {

    public SynchronizedSortedSet() {
    }

    @java.io.Serial
    static final public long serialVersionUID = 8695801310862127406L;

    @SuppressWarnings("serial")
    public SortedSet<E> ss;

    public SynchronizedSortedSet(SortedSet<E> s) {
      super(s);
      ss = s;
    }

    public SynchronizedSortedSet(SortedSet<E> s, Object mutex) {
      super(s, mutex);
      ss = s;
    }

    public Comparator<? super E> comparator() {
      synchronized (mutex) {
        return ss.comparator();
      }
    }

    public SortedSet<E> subSet(E fromElement, E toElement) {
      synchronized (mutex) {
        return new SynchronizedSortedSet<>(ss.subSet(fromElement, toElement), mutex);
      }
    }

    public SortedSet<E> headSet(E toElement) {
      synchronized (mutex) {
        return new SynchronizedSortedSet<>(ss.headSet(toElement), mutex);
      }
    }

    public SortedSet<E> tailSet(E fromElement) {
      synchronized (mutex) {
        return new SynchronizedSortedSet<>(ss.tailSet(fromElement), mutex);
      }
    }

    public E first() {
      synchronized (mutex) {
        return ss.first();
      }
    }

    public E last() {
      synchronized (mutex) {
        return ss.last();
      }
    }
  }

  static public Lock dbLock() {
    return db_mainConcepts().lock;
  }

  static public Lock dbLock(Concepts cc) {
    return cc == null ? null : cc.lock;
  }

  static public Lock dbLock(Concept c) {
    return dbLock(c == null ? null : c._concepts);
  }

  static public boolean bareDBMode_on = false;

  static public void bareDBMode() {
    bareDBMode(null);
  }

  static public void bareDBMode(Integer autoSaveInterval) {
    bareDBMode_on = true;
    conceptsAndBot(autoSaveInterval);
  }

  static public List<String> javaTokForStructure(String s) {
    return javaTok_noMLS(s);
  }

  static public Map<Integer, Integer> getBracketMap(List tok) {
    return getBracketMap(tok, getBracketMap_opening, getBracketMap_closing);
  }

  static public Map<Integer, Integer> getBracketMap(List tok, Collection<String> opening, Collection<String> closing) {
    return getBracketMap(tok, opening, closing, 0, l(tok));
  }

  static public Map<Integer, Integer> getBracketMap(List tok, Collection<String> opening, Collection<String> closing, int from, int to) {
    TreeMap<Integer, Integer> map = new TreeMap();
    List<Integer> stack = new ArrayList();
    for (int i = from | 1; i < to; i += 2) {
      Object t = tok.get(i);
      if (opening.contains(t))
        stack.add(i);
      else if (closing.contains(t))
        if (!empty(stack))
          map.put(liftLast(stack), i);
    }
    return map;
  }

  static public Map<Integer, Integer> getBracketMap(List<String> tok, IF1<String, Boolean> opening, IF1<String, Boolean> closing) {
    return getBracketMap(tok, opening, closing, 0, l(tok));
  }

  static public Map<Integer, Integer> getBracketMap(List<String> tok, IF1<String, Boolean> opening, IF1<String, Boolean> closing, int from, int to) {
    TreeMap<Integer, Integer> map = new TreeMap();
    List<Integer> stack = new ArrayList();
    for (int i = from | 1; i < to; i += 2) {
      String t = tok.get(i);
      if (opening.get(t))
        stack.add(i);
      else if (closing.get(t))
        if (!empty(stack))
          map.put(liftLast(stack), i);
    }
    return map;
  }

  static public Set<String> getBracketMap_opening = lithashset("{", "(");

  static public Set<String> getBracketMap_closing = lithashset("}", ")");

  static public boolean isOpeningBracket(String s) {
    return eqOneOf(s, "(", "{", "[");
  }

  static public boolean isClosingBracket(String s) {
    return eqOneOf(s, ")", "}", "]");
  }

  static public int clampToInt(long l) {
    return (int) clamp(l, Integer.MIN_VALUE, Integer.MAX_VALUE);
  }

  static public boolean tokenRangeLongerThanNChars(List<String> tok, int iStart, int iEnd, int maxChars) {
    int nChars = 0, n = l(tok);
    iStart = Math.max(0, iStart);
    iEnd = Math.min(n, iEnd);
    for (int i = iStart; i < iEnd; i++) {
      nChars += l(tok.get(i));
      if (nChars > maxChars)
        return true;
    }
    return false;
  }

  static public String shortenJoinSubList(int max, List<String> l, int i, int j) {
    return shortenJoin(max, subList(l, i, j));
  }

  static public String shortenJoinSubList(int max, List<String> l, int i) {
    return shortenJoin(max, subList(l, i));
  }

  static public String spaces(int n) {
    return rep(' ', n);
  }

  static public <A> Value<A> value(A a) {
    return new Value<A>(a);
  }

  static public <A, B> Map.Entry<A, B> simpleMapEntry(A key, B value) {
    return new Map.Entry<A, B>() {

      public A getKey() {
        return key;
      }

      public B getValue() {
        return value;
      }

      public B setValue(B newValue) {
        throw unimplemented();
      }
    };
  }

  static public <A, B> A floorKey(NavigableMap<A, B> map, A key) {
    if (map == null)
      return null;
    return map.floorKey(key);
  }

  static public <A, B> Map.Entry<A, B> higherEntry(NavigableMap<A, B> map, A key) {
    return map == null ? null : map.higherEntry(key);
  }

  static public <A, B> Map.Entry<A, B> lastEntry(NavigableMap<A, B> map) {
    return map == null ? null : map.lastEntry();
  }

  static public <A, B> SortedMap<A, B> subMap(SortedMap<A, B> map, A fromKey, A toKey) {
    return map == null ? null : map.subMap(fromKey, toKey);
  }

  static public <A> A second(List<A> l) {
    return get(l, 1);
  }

  static public <A> A second(Iterable<A> l) {
    if (l == null)
      return null;
    Iterator<A> it = iterator(l);
    if (!it.hasNext())
      return null;
    it.next();
    return it.hasNext() ? it.next() : null;
  }

  static public <A> A second(A[] bla) {
    return bla == null || bla.length <= 1 ? null : bla[1];
  }

  static public <A, B> B second(Pair<A, B> p) {
    return p == null ? null : p.b;
  }

  static public <A, B, C> B second(T3<A, B, C> t) {
    return t == null ? null : t.b;
  }

  static public <A> A second(Producer<A> p) {
    if (p == null)
      return null;
    if (p.next() == null)
      return null;
    return p.next();
  }

  static public char second(String s) {
    return charAt(s, 1);
  }

  static public <A, B> List<B> getAll(Map<A, B> map, Collection<A> l) {
    return lookupAllOpt(map, l);
  }

  static public <A, B> List<B> getAll(Collection<A> l, Map<A, B> map) {
    return lookupAllOpt(map, l);
  }

  static public <A, B extends IF0<A>> List<A> getAll(Iterable<B> l) {
    return getVars(l);
  }

  static public NavigableMap synchroTreeMap() {
    return synchroNavigableMap(new TreeMap());
  }

  static public boolean hasConcept(Class<? extends Concept> c, Object... params) {
    return hasConcept(db_mainConcepts(), c, params);
  }

  static public boolean hasConcept(Concepts cc, Class<? extends Concept> c, Object... params) {
    return findConceptWhere(cc, c, params) != null;
  }

  static public void clearConcepts() {
    db_mainConcepts().clearConcepts();
  }

  static public void clearConcepts(Concepts concepts) {
    concepts.clearConcepts();
  }

  static public Map unstructureMap(String s) {
    return (Map) unstructure(s);
  }

  static public Map unstructureMap(String text, boolean allDynamic, Object classFinder) {
    return (Map) unstructure(text, allDynamic, classFinder);
  }

  static public boolean exceptionMessageContains(Throwable e, String s) {
    return cic(getInnerMessage(e), s);
  }

  static public void printShortException(Throwable e) {
    print(exceptionToStringShort(e));
  }

  static public void printShortException(String s, Throwable e) {
    print(s, exceptionToStringShort(e));
  }

  static public Object unstructureGZFile(File f) {
    return unstructureGZFile(f, null);
  }

  static public Object unstructureGZFile(File f, IF1<String, Class> classFinder) {
    try {
      if (!fileExists(f))
        return null;
      BufferedReader reader = utf8BufferedReader(gzInputStream(f));
      try {
        return unstructure_tok(javaTokC_noMLS_onReader(reader), false, classFinder);
      } finally {
        _close(reader);
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public File conceptsFile(String progID) {
    return getProgramFile(progID, conceptsFileName());
  }

  static public File conceptsFile() {
    return conceptsFile(dbProgramID());
  }

  static public File conceptsFile(Concepts concepts) {
    return concepts.conceptsFile();
  }

  static public IF1 toIF1(final Object f) {
    if (f == null)
      return null;
    if (f instanceof IF1)
      return (IF1) f;
    if (isString(f)) {
      throw fail("callF_legacy");
    }
    return new IF1() {

      public Object get(Object a) {
        return callF(f, a);
      }
    };
  }

  static public int done_minPrint = 10;

  static public long done(long startTime, String desc) {
    long time = now() - startTime;
    if (time >= done_minPrint)
      print(desc + " [" + time + " ms]");
    return time;
  }

  static public long done(String desc, long startTime) {
    return done(startTime, desc);
  }

  static public long done(long startTime) {
    return done(startTime, "");
  }

  static public RemoteDB connectToDBOpt(String dbNameOrID) {
    try {
      return new RemoteDB(dbNameOrID);
    } catch (Throwable __e) {
      return null;
    }
  }

  static public <A, B> List<Pair<A, B>> mapToPairs(Map<A, B> map) {
    List<Pair<A, B>> l = emptyList(l(map));
    if (map != null)
      for (Map.Entry<A, B> e : map.entrySet()) l.add(pair(e.getKey(), e.getValue()));
    return l;
  }

  static public String dynShortName(Object o) {
    return shortDynamicClassName(o);
  }

  static public Concept getConcept(long id) {
    return db_mainConcepts().getConcept(id);
  }

  static public Concept getConcept(Concepts concepts, long id) {
    return concepts.getConcept(id);
  }

  static public <A extends Concept> A getConcept(Class<A> cc, long id) {
    return getConcept(db_mainConcepts(), cc, id);
  }

  static public <A extends Concept> A getConcept(Concepts concepts, Class<A> cc, long id) {
    Concept c = concepts.getConcept(id);
    if (c == null)
      return null;
    if (!isInstance(cc, c))
      throw fail("Can't convert concept: " + getClassName(c) + " -> " + getClassName(cc) + " (" + id + ")");
    return (A) c;
  }

  static public <A, B> A lastKey(NavigableMap<A, B> map) {
    return empty(map) ? null : map.lastKey();
  }

  static public File conceptsDir() {
    return conceptsDir(db_mainConcepts());
  }

  static public File conceptsDir(Concepts cc) {
    return cc.conceptsDir();
  }

  static public File conceptsDir(String subName) {
    return conceptsDir(db_mainConcepts(), subName);
  }

  static public File conceptsDir(Concepts cc, String subName) {
    return newFile(conceptsDir(cc), subName);
  }

  static public File dirOfFile(File f) {
    return f == null ? null : f.getParentFile();
  }

  static public File getProgramFile(String progID, String fileName) {
    if (new File(fileName).isAbsolute())
      return new File(fileName);
    return new File(getProgramDir(progID), fileName);
  }

  static public File getProgramFile(String fileName) {
    return getProgramFile(getProgramID(), fileName);
  }

  static public void callRunnableWithWrapper(Object wrapper, Runnable r) {
    if (wrapper == null)
      callF(r);
    else
      callF(wrapper, r);
  }

  static public List callFAll(Collection l, Object... args) {
    return callF_all(l, args);
  }

  static public long saveGZStructureToFile(String file, Object o) {
    return saveGZStructureToFile(getProgramFile(file), o);
  }

  static public long saveGZStructureToFile(File file, Object o) {
    return saveGZStructureToFile(file, o, new structure_Data());
  }

  static public long saveGZStructureToFile(File file, Object o, structure_Data data) {
    try {
      File parentFile = file.getParentFile();
      if (parentFile != null)
        parentFile.mkdirs();
      File tempFile = tempFileFor(file);
      if (tempFile.exists())
        try {
          String saveName = tempFile.getPath() + ".saved." + now();
          copyFile(tempFile, new File(saveName));
        } catch (Throwable e) {
          printStackTrace(e);
        }
      FileOutputStream fileOutputStream = newFileOutputStream(tempFile.getPath());
      CountingOutputStream cos;
      try {
        GZIPOutputStream gos = new GZIPOutputStream(fileOutputStream);
        cos = new CountingOutputStream(gos);
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(cos, "UTF-8");
        PrintWriter printWriter = new PrintWriter(outputStreamWriter);
        structureToPrintWriter(o, printWriter, data);
        printWriter.close();
        gos.close();
        fileOutputStream.close();
      } catch (Throwable e) {
        fileOutputStream.close();
        tempFile.delete();
        throw rethrow(e);
      }
      if (file.exists() && !file.delete())
        throw new IOException("Can't delete " + file.getPath());
      if (!tempFile.renameTo(file))
        throw new IOException("Can't rename " + tempFile + " to " + file);
      return cos.getFilePointer();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public long toM(long l) {
    return (l + 1024 * 1024 - 1) / (1024 * 1024);
  }

  static public String toM(long l, int digits) {
    return formatDouble(toM_double(l), digits);
  }

  static public File saveTextFile(String fileName, String contents) throws IOException {
    File file = new File(fileName);
    mkdirsForFile(file);
    String tempFileName = fileName + "_temp";
    File tempFile = new File(tempFileName);
    if (contents != null) {
      if (tempFile.exists())
        try {
          String saveName = tempFileName + ".saved." + now();
          copyFile(tempFile, new File(saveName));
        } catch (Throwable e) {
          printStackTrace(e);
        }
      FileOutputStream fileOutputStream = newFileOutputStream(tempFile.getPath());
      try {
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8");
        PrintWriter printWriter = new PrintWriter(outputStreamWriter);
        printWriter.print(contents);
        printWriter.close();
      } finally {
        _close(fileOutputStream);
      }
    }
    if (file.exists() && !file.delete())
      throw new IOException("Can't delete " + fileName);
    if (contents != null)
      if (!tempFile.renameTo(file))
        throw new IOException("Can't rename " + tempFile + " to " + file);
    vmBus_send("wroteFile", file);
    return file;
  }

  static public File saveTextFile(File fileName, String contents) {
    try {
      saveTextFile(fileName.getPath(), contents);
      return fileName;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public String javaTokWordWrap(String s) {
    return javaTokWordWrap(120, s);
  }

  static public String javaTokWordWrap(int cols, String s) {
    int col = 0;
    List<String> tok = javaTok(s);
    for (int i = 0; i < l(tok); i++) {
      String t = tok.get(i);
      if (odd(i) && col >= cols && !containsNewLine(t))
        tok.set(i, t = rtrimSpaces(t) + "\n");
      int idx = t.lastIndexOf('\n');
      if (idx >= 0)
        col = l(t) - (idx + 1);
      else
        col += l(t);
    }
    return join(tok);
  }

  static public String ymd() {
    return ymd(now());
  }

  static public String ymd(long now) {
    return year(now) + formatInt(month(now), 2) + formatInt(dayOfMonth(now), 2);
  }

  static public String ymd(long now, TimeZone tz) {
    return year(now, tz) + formatInt(month(now, tz), 2) + formatInt(dayOfMonth(now, tz), 2);
  }

  static public String formatInt(int i, int digits) {
    return padLeft(str(i), '0', digits);
  }

  static public String formatInt(long l, int digits) {
    return padLeft(str(l), '0', digits);
  }

  static public int hours() {
    return hours(java.util.Calendar.getInstance());
  }

  static public int hours(java.util.Calendar c) {
    return c.get(java.util.Calendar.HOUR_OF_DAY);
  }

  static public int hours(long time) {
    return hours(calendarFromTime(time));
  }

  static public int hours(long time, TimeZone tz) {
    return hours(calendarFromTime(time, tz));
  }

  static public int roundDownTo_rev(int x, int n) {
    return roundDownTo(n, x);
  }

  static public long roundDownTo_rev(long x, long n) {
    return roundDownTo(n, x);
  }

  static public int minutes() {
    return minutes(java.util.Calendar.getInstance());
  }

  static public int minutes(java.util.Calendar c) {
    return c.get(java.util.Calendar.MINUTE);
  }

  static public File copyFile(File src, File dest) {
    try {
      FileInputStream inputStream = new FileInputStream(src.getPath());
      FileOutputStream outputStream = newFileOutputStream(dest.getPath());
      try {
        copyStream(inputStream, outputStream);
        inputStream.close();
      } finally {
        outputStream.close();
      }
      return dest;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public long toK(long l) {
    return (l + 1023) / 1024;
  }

  static public String n(long l, String name) {
    return l + " " + trim(l == 1 ? singular(name) : getPlural(name));
  }

  static public String n(Collection l, String name) {
    return n(l(l), name);
  }

  static public String n(Map m, String name) {
    return n(l(m), name);
  }

  static public String n(Object[] a, String name) {
    return n(l(a), name);
  }

  static public String n(MultiSet ms, String name) {
    return n(l(ms), name);
  }

  static public Collection<Concept> allConcepts() {
    return db_mainConcepts().allConcepts();
  }

  static public Collection<Concept> allConcepts(Concepts concepts) {
    return concepts.allConcepts();
  }

  static public boolean isTransient(Field f) {
    return (f.getModifiers() & java.lang.reflect.Modifier.TRANSIENT) != 0;
  }

  static public FixedRateTimer doEvery_daemon(long delay, final Object r) {
    return doEvery_daemon(defaultTimerName(), delay, r);
  }

  static public FixedRateTimer doEvery_daemon(String timerName, long delay, final Object r) {
    return doEvery_daemon(timerName, delay, delay, r);
  }

  static public FixedRateTimer doEvery_daemon(long delay, long firstDelay, final Object r) {
    return doEvery_daemon(defaultTimerName(), delay, firstDelay, r);
  }

  static public FixedRateTimer doEvery_daemon(String timerName, long delay, long firstDelay, final Object r) {
    FixedRateTimer timer = new FixedRateTimer(true);
    timer.scheduleAtFixedRate(smartTimerTask(r, timer, delay), firstDelay, delay);
    return timer;
  }

  static public FixedRateTimer doEvery_daemon(double delaySeconds, final Object r) {
    return doEvery_daemon(toMS(delaySeconds), r);
  }

  static public void closeAllOpt(Iterable l) {
    if (l == null)
      return;
    for (Object c : l) {
      try {
        if (c instanceof AutoCloseable)
          close((AutoCloseable) c);
      } catch (Throwable __e) {
        pcallFail(__e);
      }
    }
  }

  static public void closeAllOpt(Object... l) {
    if (l == null)
      return;
    for (Object c : l) {
      try {
        if (c instanceof AutoCloseable)
          close((AutoCloseable) c);
      } catch (Throwable __e) {
        pcallFail(__e);
      }
    }
  }

  static public void sleepInCleanUp(long ms) {
    try {
      if (ms < 0)
        return;
      Thread.sleep(ms);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public <A> A firstOfType(Collection c, Class<A> type) {
    for (Object x : c) if (isInstanceX(type, x))
      return (A) x;
    return null;
  }

  static public List<Concept> conceptsOfType(String type) {
    return db_mainConcepts().conceptsOfType(type);
  }

  static public <A> List<A> concatLists_conservative(List<A> a, List<A> b) {
    if (empty(a))
      return b;
    if (empty(b))
      return a;
    return concatLists(a, b);
  }

  static public <A> List<A> concatLists_conservative(Collection<A> a, Collection<A> b) {
    if (empty(a) && b instanceof List)
      return ((List) b);
    if (empty(b) && a instanceof List)
      return ((List) a);
    return concatLists(a, b);
  }

  static public <A> List<A> filterByType(Iterable c, Class<A> type) {
    List<A> l = new ArrayList();
    if (c != null)
      for (Object x : c) if (isInstanceX(type, x))
        l.add((A) x);
    return l;
  }

  static public <A> List<A> filterByType(Object[] c, Class<A> type) {
    return filterByType(asList(c), type);
  }

  static public <A> List<A> filterByType(Class<A> type, Iterable c) {
    return filterByType(c, type);
  }

  static public <A> List<A> filterByDynamicType(Collection<A> c, String type) {
    List<A> l = new ArrayList();
    for (A x : c) if (eq(dynamicClassName(x), type))
      l.add(x);
    return l;
  }

  static public boolean hasType(Collection c, Class type) {
    for (Object x : c) if (isInstanceX(type, x))
      return true;
    return false;
  }

  static public <A extends Concept> A findBackRef(Concept c, Class<A> type) {
    for (Concept.Ref r : c.backRefs) if (instanceOf(r.concept(), type))
      return (A) r.concept();
    return null;
  }

  static public <A extends Concept> A findBackRef(Class<A> type, Concept c) {
    return findBackRef(c, type);
  }

  static public boolean checkConceptFields(Concept x, Object... data) {
    for (int i = 0; i < l(data); i += 2) if (neq(cget(x, (String) data[i]), deref(data[i + 1])))
      return false;
    return true;
  }

  static public <A, B> void mapRemove(Map<A, B> map, A key) {
    if (map != null && key != null)
      map.remove(key);
  }

  static public Concept cnew(String name, Object... values) {
    return cnew(db_mainConcepts(), name, values);
  }

  static public Concept cnew(Concepts concepts, String name, Object... values) {
    Class<? extends Concept> cc = findClass(name);
    concepts_unlisted.set(true);
    Concept c;
    try {
      c = cc != null ? nuObject(cc) : new Concept(name);
    } finally {
      concepts_unlisted.set(null);
    }
    csetAll(c, values);
    concepts.register(c);
    return c;
  }

  static public <A extends Concept> A cnew(Class<A> cc, Object... values) {
    return cnew(db_mainConcepts(), cc, values);
  }

  static public <A extends Concept> A cnew(Concepts concepts, Class<A> cc, Object... values) {
    concepts_unlisted.set(true);
    A c;
    try {
      c = nuObject(cc);
    } finally {
      concepts_unlisted.set(null);
    }
    csetAll(c, values);
    concepts.register(c);
    return c;
  }

  static public int cset(Concept c, Object... values) {
    try {
      if (c == null)
        return 0;
      warnIfOddCount(values = expandParams(c.getClass(), values));
      int changes = 0;
      for (int i = 0; i + 1 < l(values); i += 2) if (_csetField(c, (String) values[i], values[i + 1]))
        ++changes;
      return changes;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public int cset(Iterable<? extends Concept> l, Object... values) {
    int changes = 0;
    for (Concept c : unnullForIteration(l)) changes += cset(c, values);
    return changes;
  }

  static public <A extends Concept> int cset(Concept.Ref<A> c, Object... values) {
    return cset(getVar(c), values);
  }

  static public Object cget(Object c, String field) {
    c = derefRef(c);
    Object o = getOpt(c, field);
    return derefRef(o);
  }

  static public Object cget(String field, Object c) {
    return cget(c, field);
  }

  static public Object deref(Object o) {
    if (o instanceof IRef)
      return ((IRef) o).get();
    return o;
  }

  static public void lock(Lock lock) {
    try {
      ping();
      if (lock == null)
        return;
      try {
        vmBus_send("locking", lock, "thread", currentThread());
        lock.lockInterruptibly();
        vmBus_send("locked", lock, "thread", currentThread());
      } catch (InterruptedException e) {
        Object reason = vm_threadInterruptionReasonsMap().get(currentThread());
        print("Locking interrupted! Reason: " + strOr(reason, "Unknown"));
        printStackTrace(e);
        rethrow(e);
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public void lock(Lock lock, String msg) {
    print("Locking: " + msg);
    lock(lock);
  }

  static public void lock(Lock lock, String msg, long timeout) {
    print("Locking: " + msg);
    lockOrFail(lock, timeout);
  }

  static public ReentrantLock lock() {
    return fairLock();
  }

  static public String loadConceptsStructure(String progID) {
    return loadTextFilePossiblyGZipped(getProgramFile(progID, "concepts.structure"));
  }

  static public String loadConceptsStructure() {
    return loadConceptsStructure(dbProgramID());
  }

  static public void unlock(Lock lock, String msg) {
    if (lock == null)
      return;
    lock.unlock();
    vmBus_send("unlocked", lock, "thread", currentThread());
    print("Unlocked: " + msg);
  }

  static public void unlock(Lock lock) {
    if (lock == null)
      return;
    lock.unlock();
    vmBus_send("unlocked", lock, "thread", currentThread());
  }

  static public void cleanKillVM() {
    try {
      ping();
      assertNotOnAWTThread();
      cleanKillVM_noSleep();
      Object o = new Object();
      synchronized (o) {
        o.wait();
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public void cleanKillVM_noSleep() {
    call(getJavaX(), "cleanKill");
  }

  static public <A extends Concept> int countConcepts(Concepts concepts, Class<A> c, Object... params) {
    return concepts.countConcepts(c, params);
  }

  static public <A extends Concept> int countConcepts(Class<A> c, Object... params) {
    return db_mainConcepts().countConcepts(c, params);
  }

  static public int countConcepts() {
    return db_mainConcepts().countConcepts();
  }

  static public int countConcepts(String className) {
    return db_mainConcepts().countConcepts(className);
  }

  static public <A extends Concept> int countConcepts(Concepts concepts, String className) {
    return concepts.countConcepts(className);
  }

  static public int countConcepts(Concepts concepts) {
    return concepts.countConcepts();
  }

  static public <A> boolean syncAdd(Collection<A> c, A b) {
    if (c == null)
      return false;
    synchronized (collectionMutex(c)) {
      return c.add(b);
    }
  }

  static public <A> void syncAdd(List<A> l, int idx, A b) {
    if (l != null)
      synchronized (collectionMutex(l)) {
        l.add(idx, b);
      }
  }

  static public <A> boolean syncRemove(Collection<A> c, A b) {
    if (c == null)
      return false;
    synchronized (collectionMutex(c)) {
      return c.remove(b);
    }
  }

  static public <A> A syncRemove(List<A> l, int idx) {
    if (l == null)
      return null;
    synchronized (collectionMutex(l)) {
      return l.remove(idx);
    }
  }

  static public <A, B> B syncRemove(Map<A, B> map, A key) {
    return map == null ? null : map.remove(key);
  }

  static public <A> List<A> syncAddOrCreate(List<A> l, A a) {
    if (l == null)
      l = syncList();
    l.add(a);
    return l;
  }

  static public String nConcepts(long n) {
    return n2(n, "concept");
  }

  static public String nConcepts(Collection l) {
    return nConcepts(l(l));
  }

  static public String nConcepts(Map map) {
    return nConcepts(l(map));
  }

  static public String shortDynamicClassName(Object o) {
    if (o instanceof DynamicObject && ((DynamicObject) o).className != null)
      return ((DynamicObject) o).className;
    return shortClassName(o);
  }

  static public boolean dynamicObjectIsLoading() {
    return isUnstructuring();
  }

  static public void assertIsInstance(Class type, Object o) {
    if (!isInstance(type, o))
      throw fail(_getClass(o) + " is not a subclass of " + type);
  }

  static public void assertIsInstance(Object o, Class type) {
    assertIsInstance(type, o);
  }

  static public void change() {
    callOpt(getOptMC("mainConcepts"), "allChanged");
  }

  static public boolean has(String a, String b, String c) {
    return false;
  }

  static public boolean has(T3<String, String, String> t) {
    return false;
  }

  static public void removeLast(List l) {
    if (!l.isEmpty())
      l.remove(l(l) - 1);
  }

  static public void removeLast(List l, int n) {
    removeSubList(l, l(l) - n);
  }

  static public void removeLast(int n, List l) {
    removeLast(l, n);
  }

  static public void removeLast(StringBuilder buf) {
    if (buf == null)
      return;
    int n = buf.length();
    if (n > 0)
      buf.setLength(n - 1);
  }

  static public <A> A syncGet(List<A> l, int idx) {
    if (l == null || idx < 0)
      return null;
    synchronized (l) {
      return idx < l(l) ? l.get(idx) : null;
    }
  }

  static public <A, B> B syncGet(Map<A, B> map, A a) {
    if (map == null)
      return null;
    synchronized (map) {
      return map.get(a);
    }
  }

  static public int syncL(Collection l) {
    if (l == null)
      return 0;
    synchronized (collectionMutex(l)) {
      return l.size();
    }
  }

  static public int syncL(Map map) {
    if (map == null)
      return 0;
    synchronized (collectionMutex(map)) {
      return map.size();
    }
  }

  static public <A> List<A> syncL() {
    return syncList();
  }

  static public <A> List<A> addDyn_quickSync(List<A> l, A a) {
    if (l == null)
      l = new ArrayList();
    syncAdd(l, a);
    return l;
  }

  static public <A> List<A> removeDyn_quickSync(List<A> l, A a) {
    if (l == null)
      return null;
    synchronized (collectionMutex(l)) {
      l.remove(a);
      return empty(l) ? null : l;
    }
  }

  static public void crenameField_noOverwrite(Concept c, String oldField, String newField) {
    if (c == null || eq(oldField, newField))
      return;
    Object value = cget(c, oldField);
    if (newField != null && cget(c, newField) == null)
      cset(c, newField, value);
    cset(c, oldField, null);
  }

  static public Collection<Concept.Ref> scanConceptForRefs(Concept c) {
    Set<Concept.Ref> refs = new HashSet();
    if (c != null)
      for (Object o : values(objectToMap(c))) {
        if (o instanceof Concept.Ref)
          refs.add((Concept.Ref) o);
        else if (o instanceof Concept.RefL)
          addAll(refs, ((Concept.RefL) o).l);
      }
    return refs;
  }

  static public Concepts concepts(Concept c) {
    return c == null ? null : c._concepts;
  }

  static public String formatDouble_significant2(double d, int digits) {
    try {
      digits -= Math.floor(max(-10, Math.log10(abs(d)) + 1));
      return formatDouble(d, digits);
    } catch (Throwable _e) {
      print("Had number: " + d + ", digits: " + digits);
      throw rethrow(_e);
    }
  }

  static public double fileAgeInSeconds(File f) {
    return f == null ? -1 : msToSeconds(now() - fileModificationTime(f));
  }

  static public String nSeconds(long n) {
    return n2(n, "second");
  }

  static public String nSeconds(Collection l) {
    return nSeconds(l(l));
  }

  static public int iceil(double d) {
    return (int) Math.ceil(d);
  }

  static public boolean deleteFile(File file) {
    return file != null && file.delete();
  }

  public static File mkdirsForFile(File file) {
    File dir = file.getParentFile();
    if (dir != null) {
      dir.mkdirs();
      if (!dir.isDirectory())
        if (dir.isFile())
          throw fail("Please delete the file " + f2s(dir) + " - it is supposed to be a directory!");
        else
          throw fail("Unknown IO exception during mkdirs of " + f2s(file));
    }
    return file;
  }

  public static String mkdirsForFile(String path) {
    mkdirsForFile(new File(path));
    return path;
  }

  static public Path toPath(File f) {
    return f == null ? null : f.toPath();
  }

  static public void printExceptionShort(Throwable e) {
    printExceptionShort("", e);
  }

  static public void printExceptionShort(String prefix, Throwable e) {
    print(prefix, exceptionToStringShort(e));
  }

  static public File saveTextFileWithoutTemp(File file, String contents) {
    try {
      mkdirsForFile(file);
      var fileOutputStream = newFileOutputStream(file);
      try {
        var outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8");
        var printWriter = new PrintWriter(outputStreamWriter);
        printWriter.print(unnull(contents));
        printWriter.close();
        return file;
      } finally {
        _close(fileOutputStream);
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public FixedRateTimer doEvery(long delay, final Object r) {
    return doEvery(delay, delay, r);
  }

  static public FixedRateTimer doEvery(long delay, long firstDelay, final Object r) {
    FixedRateTimer timer = new FixedRateTimer(shorten(programID() + ": " + r, 80));
    timer.scheduleAtFixedRate(smartTimerTask(r, timer, toInt(delay)), toInt(firstDelay), toInt(delay));
    return vmBus_timerStarted(timer);
  }

  static public FixedRateTimer doEvery(double initialSeconds, double delaySeconds, final Object r) {
    return doEvery(toMS(delaySeconds), toMS(initialSeconds), r);
  }

  static public FixedRateTimer doEvery(double delaySeconds, final Object r) {
    return doEvery(toMS(delaySeconds), r);
  }

  static public File touchFile(File file) {
    try {
      closeRandomAccessFile(newRandomAccessFile(mkdirsForFile(file), "rw"));
      return file;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public <A, B> NavigableSet<A> navigableKeys(NavigableMap<A, B> map) {
    return map == null ? new TreeSet() : map.navigableKeySet();
  }

  static public <A> NavigableSet<A> navigableKeys(MultiSet<A> ms) {
    return ((NavigableMap) ms.map).navigableKeySet();
  }

  static public <A, B> NavigableSet<A> navigableKeys(MultiMap<A, B> mm) {
    return ((NavigableMap) mm.data).navigableKeySet();
  }

  static public <A, B> Map<A, List<B>> toMap(MultiMap<A, B> m) {
    return multiMapToMap(m);
  }

  static public String dbBotName(String progIDWithCase) {
    return fsI_flex(progIDWithCase) + " Concepts";
  }

  static public Map<String, Integer> findBot_cache = synchroHashMap();

  static public int findBot_timeout = 5000;

  static public DialogIO findBot(String searchPattern) {
    String subBot = null;
    int i = searchPattern.indexOf('/');
    if (i >= 0 && (isJavaIdentifier(searchPattern.substring(0, i)) || isInteger(searchPattern.substring(0, i)))) {
      subBot = searchPattern.substring(i + 1);
      searchPattern = searchPattern.substring(0, i);
      if (!isInteger(searchPattern))
        searchPattern = "Multi-Port at " + searchPattern + ".";
    }
    if (isInteger(searchPattern))
      return talkToSubBot(subBot, talkTo(parseInt(searchPattern)));
    if (eq(searchPattern, "remote"))
      return talkToSubBot(subBot, talkTo("second.tinybrain.de", 4999));
    Integer port = findBot_cache.get(searchPattern);
    if (port != null)
      try {
        DialogIO io = talkTo("localhost", port);
        io.waitForLine();
        String line = io.readLineNoBlock();
        if (indexOfIgnoreCase(line, searchPattern) == 0) {
          call(io, "pushback", line);
          return talkToSubBot(subBot, io);
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    List<ProgramScan.Program> bots = quickBotScan();
    for (ProgramScan.Program p : bots) {
      if (indexOfIgnoreCase(p.helloString, searchPattern) == 0) {
        findBot_cache.put(searchPattern, p.port);
        return talkToSubBot(subBot, talkTo("localhost", p.port));
      }
    }
    for (ProgramScan.Program p : bots) {
      String botName = firstPartOfHelloString(p.helloString);
      boolean isVM = startsWithIgnoreCase(p.helloString, "This is a JavaX VM.");
      boolean shouldRecurse = startsWithIgnoreCase(botName, "Multi-Port") || isVM;
      if (shouldRecurse)
        try {
          Map<Number, String> subBots = (Map) unstructure(sendToLocalBotQuietly(p.port, "list bots"));
          for (Number vport : subBots.keySet()) {
            String name = subBots.get(vport);
            if (startsWithIgnoreCase(name, searchPattern))
              return talkToSubBot(vport.longValue(), talkTo("localhost", p.port));
          }
        } catch (Throwable __e) {
          print(exceptionToStringShort(__e));
        }
    }
    return null;
  }

  static public void nohupJavax(final String javaxargs) {
    {
      startThread(new Runnable() {

        public void run() {
          try {
            call(hotwireOnce("#1008562"), "nohupJavax", javaxargs);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "call(hotwireOnce(\"#1008562\"), \"nohupJavax\", javaxargs);";
        }
      });
    }
  }

  static public void nohupJavax(final String javaxargs, final String vmArgs) {
    {
      startThread(new Runnable() {

        public void run() {
          try {
            call(hotwireOnce("#1008562"), "nohupJavax", javaxargs, vmArgs);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "call(hotwireOnce(\"#1008562\"), \"nohupJavax\", javaxargs, vmArgs);";
        }
      });
    }
  }

  static public String fsI(String id) {
    return formatSnippetID(id);
  }

  static public String fsI(long id) {
    return formatSnippetID(id);
  }

  static public long waitForBotStartUp_timeoutSeconds = 60;

  static public String waitForBotStartUp(String botName) {
    for (int i = 0; i < waitForBotStartUp_timeoutSeconds; i++) {
      sleepSeconds(i == 0 ? 0 : 1);
      String addr = getBotAddress(botName);
      if (addr != null)
        return addr;
    }
    throw fail("Bot not found: " + quote(botName));
  }

  static public Object rpc(String botName, String method, Object... args) {
    return unstructure_matchOK2OrFail(sendToLocalBot(botName, rpc_makeCall(method, args)));
  }

  static public Object rpc(DialogIO bot, String method, Object... args) {
    return unstructure_matchOK2OrFail(bot.ask(rpc_makeCall(method, args)));
  }

  static public String rpc_makeCall(String method, Object... args) {
    if (empty(args))
      return "call " + method;
    return format("call *", concatLists((List) ll(method), asList(args)));
  }

  static public <A extends Concept> A uniq(Class<A> c, Object... params) {
    return uniqueConcept(c, params);
  }

  static public <A extends Concept> A uniq(Concepts cc, Class<A> c, Object... params) {
    return uniqueConcept(cc, c, params);
  }

  static public void close(AutoCloseable c) {
    _close(c);
  }

  static public String lower(String s) {
    return s == null ? null : s.toLowerCase();
  }

  static public char lower(char c) {
    return Character.toLowerCase(c);
  }

  static public double floor(double d) {
    return Math.floor(d);
  }

  static volatile public boolean readLine_noReadLine = false;

  static public String readLine_lastInput;

  static public String readLine_prefix = "[] ";

  static public String readLine() {
    if (readLine_noReadLine)
      return null;
    String s = readLineHidden();
    if (s != null) {
      readLine_lastInput = s;
      print(readLine_prefix + s);
    }
    return s;
  }

  static public String format3(String pat, Object... args) {
    if (args.length == 0)
      return pat;
    List<String> tok = javaTokPlusPeriod(pat);
    int argidx = 0;
    for (int i = 1; i < tok.size(); i += 2) if (tok.get(i).equals("*"))
      tok.set(i, format3_formatArg(argidx < args.length ? args[argidx++] : "null"));
    return join(tok);
  }

  static public String format3_formatArg(Object arg) {
    if (arg == null)
      return "null";
    if (arg instanceof String) {
      String s = (String) arg;
      return isIdentifier(s) || isNonNegativeInteger(s) ? s : quote(s);
    }
    if (arg instanceof Integer || arg instanceof Long)
      return String.valueOf(arg);
    return quote(structure(arg));
  }

  volatile static public boolean conceptsAndBot_running = false;

  static public boolean conceptsAndBot_thinOnStart = true;

  static public void conceptsAndBot() {
    conceptsAndBot(null);
  }

  static public void conceptsAndBot(Integer autoSaveInterval) {
    if (conceptsAndBot_running)
      return;
    conceptsAndBot_running = true;
    Concepts cc = db_mainConcepts();
    cc.programID = getDBProgramID();
    try {
      if (cc.useFileLock) {
        if (!cc.fileLock().tryToLock()) {
          ensureDBNotRunning(dbBotStandardName());
          cc.fileLock().forceLock();
        }
      } else
        ensureDBNotRunning(dbBotStandardName());
    } catch (Throwable e) {
      printStackTrace(e);
      cc.dontSave = true;
      throw rethrow(e);
    }
    cc.persist(autoSaveInterval);
    dbBot(false);
    if (conceptsAndBot_thinOnStart) {
      try {
        thinAProgramsBackups(getDBProgramID(), true);
      } catch (Throwable __e) {
        pcallFail(__e);
      }
    }
  }

  static public String shortenJoin(int max, Iterable<String> l) {
    StringBuilder buf = new StringBuilder();
    for (String s : unnullForIteration(l)) {
      buf.append(s);
      if (l(buf) > max)
        break;
    }
    return shorten(max, str(buf));
  }

  static public RuntimeException unimplemented() {
    throw fail("TODO");
  }

  static public RuntimeException unimplemented(String msg) {
    throw fail("TODO: " + msg);
  }

  static public RuntimeException unimplemented(Object obj) {
    throw fail("TODO: implement method in " + className(obj));
  }

  static public char charAt(String s, int i) {
    return s != null && i >= 0 && i < s.length() ? s.charAt(i) : '\0';
  }

  static public <A, B> List<B> lookupAllOpt(Map<A, B> map, Collection<A> l) {
    List<B> out = new ArrayList();
    if (l != null)
      for (A a : l) addIfNotNull(out, map.get(a));
    return out;
  }

  static public <A, B> List<B> lookupAllOpt(Collection<A> l, Map<A, B> map) {
    return lookupAllOpt(map, l);
  }

  static public <A, B extends IF0<A>> List<A> getVars(Iterable<B> l) {
    return lambdaMap(__75 -> getVar(__75), l);
  }

  static public <A extends Concept> A findConceptWhere(Class<A> c, Object... params) {
    return findConceptWhere(db_mainConcepts(), c, params);
  }

  static public <A extends Concept> A findConceptWhere(Concepts concepts, Class<A> c, Object... params) {
    ping();
    params = expandParams(c, params);
    if (concepts.fieldIndices != null)
      for (int i = 0; i < l(params); i += 2) {
        IFieldIndex<A, Object> index = concepts.getFieldIndex(c, (String) params[i]);
        if (index != null) {
          for (A x : index.getAll(params[i + 1])) if (checkConceptFields(x, params))
            return x;
          return null;
        }
      }
    for (A x : concepts.list(c)) if (checkConceptFields(x, params))
      return x;
    return null;
  }

  static public Concept findConceptWhere(Concepts concepts, String c, Object... params) {
    for (Concept x : concepts.list(c)) if (checkConceptFields(x, params))
      return x;
    return null;
  }

  static public boolean cic(Collection<String> l, String s) {
    return containsIgnoreCase(l, s);
  }

  static public boolean cic(Collection<Symbol> l, Symbol s) {
    return contains(l, s);
  }

  static public boolean cic(String[] l, String s) {
    return containsIgnoreCase(l, s);
  }

  static public boolean cic(String s, char c) {
    return containsIgnoreCase(s, c);
  }

  static public boolean cic(String a, String b) {
    return containsIgnoreCase(a, b);
  }

  static public String getInnerMessage(Throwable e) {
    if (e == null)
      return null;
    return getInnerException(e).getMessage();
  }

  static public String conceptsFileName() {
    return "concepts.structure.gz";
  }

  static public String dbProgramID() {
    return getDBProgramID();
  }

  static public boolean isString(Object o) {
    return o instanceof String;
  }

  static public File getProgramDir() {
    return programDir();
  }

  static public File getProgramDir(String snippetID) {
    return programDir(snippetID);
  }

  static public List callF_all(Collection l, Object... args) {
    return map(l, f -> callF(f, args));
  }

  static public File tempFileFor(File f) {
    return new File(f.getPath() + "_temp");
  }

  static public FileOutputStream newFileOutputStream(File path) throws IOException {
    return newFileOutputStream(path.getPath());
  }

  static public FileOutputStream newFileOutputStream(String path) throws IOException {
    return newFileOutputStream(path, false);
  }

  static public FileOutputStream newFileOutputStream(File path, boolean append) throws IOException {
    return newFileOutputStream(path.getPath(), append);
  }

  static public FileOutputStream newFileOutputStream(String path, boolean append) throws IOException {
    mkdirsForFile(path);
    FileOutputStream f = new FileOutputStream(path, append);
    _registerIO(f, path, true);
    return f;
  }

  static public double toM_double(long l) {
    return l / (1024 * 1024.0);
  }

  public static String rtrimSpaces(String s) {
    if (s == null)
      return null;
    int i = s.length();
    while (i > 0 && " \t".indexOf(s.charAt(i - 1)) >= 0) --i;
    return i < s.length() ? s.substring(0, i) : s;
  }

  static public int year() {
    return localYear();
  }

  static public int year(Timestamp now) {
    return localYear(toLong(now));
  }

  static public int year(long now) {
    return localYear(now);
  }

  static public int year(long now, TimeZone tz) {
    return parseInt(simpleDateFormat("y", tz).format(now));
  }

  static public int month() {
    return localMonth();
  }

  static public int month(Timestamp now) {
    return localMonth(toLong(now));
  }

  static public int month(long now) {
    return localMonth(now);
  }

  static public int month(long now, TimeZone tz) {
    return parseInt(simpleDateFormat("M", tz).format(now));
  }

  static public int dayOfMonth() {
    return localDayOfMonth();
  }

  static public int dayOfMonth(long now) {
    return localDayOfMonth(now);
  }

  static public int dayOfMonth(long now, TimeZone tz) {
    return parseInt(simpleDateFormat("d", tz).format(now));
  }

  static public String padLeft(String s, char c, int n) {
    return rep(c, n - l(s)) + s;
  }

  static public String padLeft(String s, int n) {
    return padLeft(s, ' ', n);
  }

  static public java.util.Calendar calendarFromTime(long time, TimeZone tz) {
    java.util.Calendar c = java.util.Calendar.getInstance(tz);
    c.setTimeInMillis(time);
    return c;
  }

  static public java.util.Calendar calendarFromTime(long time) {
    java.util.Calendar c = java.util.Calendar.getInstance();
    c.setTimeInMillis(time);
    return c;
  }

  static public int roundDownTo(int n, int x) {
    return x / n * n;
  }

  static public long roundDownTo(long n, long x) {
    return x / n * n;
  }

  static public double roundDownTo(double n, double x) {
    return floor(x / n) * n;
  }

  static public void copyStream(InputStream in, OutputStream out) {
    try {
      byte[] buf = new byte[65536];
      while (true) {
        int n = in.read(buf);
        if (n <= 0)
          return;
        out.write(buf, 0, n);
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Map<String, String> singular_specials = litmap("children", "child", "images", "image", "chess", "chess");

  static public Set<String> singular_specials2 = litciset("time", "machine", "line", "rule");

  static public String singular(String s) {
    if (s == null)
      return null;
    {
      String __1 = singular_specials.get(s);
      if (!empty(__1))
        return __1;
    }
    if (singular_specials2.contains(dropSuffix("s", afterLastSpace(s))))
      return dropSuffix("s", s);
    if (s.endsWith("ness"))
      return s;
    if (s.endsWith("ges"))
      return dropSuffix("s", s);
    if (endsWith(s, "bases"))
      return dropLast(s);
    s = dropSuffix("es", s);
    s = dropSuffix("s", s);
    return s;
  }

  static public String defaultTimerName_name;

  static public String defaultTimerName() {
    if (defaultTimerName_name == null)
      defaultTimerName_name = "A timer by " + programID();
    return defaultTimerName_name;
  }

  static public TimerTask smartTimerTask(Object r, java.util.Timer timer, long delay) {
    return new SmartTimerTask(r, timer, delay, _threadInfo());
  }

  static public class SmartTimerTask extends TimerTask implements IFieldsToList {

    public Object r;

    public java.util.Timer timer;

    public long delay;

    public Object threadInfo;

    public SmartTimerTask() {
    }

    public SmartTimerTask(Object r, java.util.Timer timer, long delay, Object threadInfo) {
      this.threadInfo = threadInfo;
      this.delay = delay;
      this.timer = timer;
      this.r = r;
    }

    public String toString() {
      return shortClassName_dropNumberPrefix(this) + "(" + r + ", " + timer + ", " + delay + ", " + threadInfo + ")";
    }

    public Object[] _fieldsToList() {
      return new Object[] { r, timer, delay, threadInfo };
    }

    public long lastRun;

    public void run() {
      if (!licensed())
        timer.cancel();
      else {
        _threadInheritInfo(threadInfo);
        AutoCloseable __1 = tempActivity(r);
        try {
          lastRun = fixTimestamp(lastRun);
          long now = now();
          if (now >= lastRun + delay * 0.9) {
            lastRun = now;
            if (eq(false, pcallF(r)))
              timer.cancel();
          }
        } finally {
          _close(__1);
        }
      }
    }
  }

  static public String dynamicClassName(Object o) {
    if (o instanceof DynamicObject && ((DynamicObject) o).className != null)
      return "main$" + ((DynamicObject) o).className;
    return className(o);
  }

  static public boolean instanceOf(Object o, String className) {
    if (o == null)
      return false;
    String c = o.getClass().getName();
    return eq(c, className) || eq(c, "main$" + className);
  }

  static public boolean instanceOf(Object o, Class c) {
    if (c == null)
      return false;
    return c.isInstance(o);
  }

  static public boolean instanceOf(Class c, Object o) {
    return instanceOf(o, c);
  }

  static public HashMap<String, Class> findClass_cache = new HashMap();

  static public Class findClass(String name) {
    synchronized (findClass_cache) {
      if (findClass_cache.containsKey(name))
        return findClass_cache.get(name);
      if (!isJavaIdentifier(name))
        return null;
      Class c;
      try {
        c = Class.forName("main$" + name);
      } catch (ClassNotFoundException e) {
        c = null;
      }
      findClass_cache.put(name, c);
      return c;
    }
  }

  static public int csetAll(Concept c, Object... values) {
    return cset(c, values);
  }

  static public int csetAll(Iterable<? extends Concept> l, Object... values) {
    int n = 0;
    for (Concept c : unnullForIteration(l)) n += cset(c, values);
    return n;
  }

  static public int csetAll(Concept c, Map<String, Object> values) {
    int n = 0;
    for (Map.Entry<? extends String, ? extends Object> __0 : _entrySet(values)) {
      String field = __0.getKey();
      Object value = __0.getValue();
      n += cset(c, field, value);
    }
    return n;
  }

  static public <A extends Concept> Object[] expandParams(Class<A> c, Object[] params) {
    if (l(params) == 1)
      params = new Object[] { singleFieldName(c), params[0] };
    else
      warnIfOddCount(params);
    return params;
  }

  static public boolean _csetField(Concept c, String field, Object value) {
    try {
      Field f = setOpt_findField(c.getClass(), field);
      if (value instanceof RC)
        value = c._concepts.getConcept((RC) value);
      value = deref(value);
      if (value instanceof String && l((String) value) >= concepts_internStringsLongerThan)
        value = intern((String) value);
      if (f == null) {
        assertIdentifier(field);
        Object oldVal = mapGet(c.fieldValues, field);
        if (value instanceof Concept) {
          if (oldVal instanceof Concept.Ref)
            return ((Concept.Ref) oldVal).set((Concept) value);
          else {
            dynamicObject_setRawFieldValue(c, field, c.new Ref((Concept) value));
            c.change();
            return true;
          }
        } else {
          if (oldVal instanceof Concept.Ref)
            ((Concept.Ref) oldVal).unindexAndDrop();
          if (eq(oldVal, value))
            return false;
          if (isConceptList(value) && nempty(((List) value))) {
            dynamicObject_setRawFieldValue(c, field, c.new RefL(((List) value)));
            c.change();
            return true;
          }
          if (value == null) {
            dynamicObject_dropRawField(c, field);
          } else {
            if (!isPersistable(value))
              throw fail("Can't persist: " + c + "." + field + " = " + value);
            dynamicObject_setRawFieldValue(c, field, value);
          }
          c.change();
          return true;
        }
      } else if (isSubtypeOf(f.getType(), Concept.Ref.class)) {
        ((Concept.Ref) f.get(c)).set((Concept) derefRef(value));
        c.change();
        return true;
      } else if (isSubtypeOf(f.getType(), Concept.RefL.class)) {
        ((Concept.RefL) f.get(c)).replaceWithList(lmap(__76 -> derefRef(__76), (List) value));
        c.change();
        return true;
      } else {
        Object old = f.get(c);
        if (neq(value, old)) {
          boolean isTransient = isTransient(f);
          if (!isTransient && !isPersistable(value))
            throw fail("Can't persist: " + c + "." + field + " = " + value);
          f.set(c, value);
          if (!isTransient)
            c.change();
          return true;
        }
      }
      return false;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Object derefRef(Object o) {
    if (o instanceof Concept.Ref)
      o = ((Concept.Ref) o).get();
    return o;
  }

  static public <A extends Concept> A derefRef(Concept.Ref<A> r) {
    return r == null ? null : r.get();
  }

  static public String strOr(Object o, String ifNull) {
    return o == null ? ifNull : str(o);
  }

  static public void lockOrFail(Lock lock, long timeout) {
    try {
      ping();
      vmBus_send("locking", lock, "thread", currentThread());
      if (!lock.tryLock(timeout, TimeUnit.MILLISECONDS)) {
        String s = "Couldn't acquire lock after " + timeout + " ms.";
        if (lock instanceof ReentrantLock) {
          ReentrantLock l = (ReentrantLock) lock;
          s += " Hold count: " + l.getHoldCount() + ", owner: " + call(l, "getOwner");
        }
        throw fail(s);
      }
      vmBus_send("locked", lock, "thread", currentThread());
      ping();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public ReentrantLock fairLock() {
    return new ReentrantLock(true);
  }

  static public String loadTextFilePossiblyGZipped(String fileName) {
    return loadTextFilePossiblyGZipped(fileName, null);
  }

  static public String loadTextFilePossiblyGZipped(String fileName, String defaultContents) {
    File gz = new File(fileName + ".gz");
    return gz.exists() ? loadGZTextFile(gz) : loadTextFile(fileName, defaultContents);
  }

  static public String loadTextFilePossiblyGZipped(File fileName) {
    return loadTextFilePossiblyGZipped(fileName, null);
  }

  static public String loadTextFilePossiblyGZipped(File fileName, String defaultContents) {
    return loadTextFilePossiblyGZipped(fileName.getPath(), defaultContents);
  }

  static public void assertNotOnAWTThread() {
    assertFalse("Can't do this in AWT thread", isAWTThread());
  }

  static public <A> List<A> syncList() {
    return synchroList();
  }

  static public <A> List<A> syncList(List<A> l) {
    return synchroList(l);
  }

  static public boolean isUnstructuring() {
    return isTrue(getTL(dynamicObjectIsLoading_threadLocal()));
  }

  static public Object getOptMC(String field) {
    return getOpt(mc(), field);
  }

  static public Map<String, Object> objectToMap(Object o) {
    try {
      if (o instanceof Map)
        return (Map) o;
      if (o == null)
        return null;
      TreeMap<String, Object> map = new TreeMap();
      Class c = o.getClass();
      while (c != Object.class) {
        Field[] fields = c.getDeclaredFields();
        for (final Field field : fields) {
          if ((field.getModifiers() & Modifier.STATIC) != 0)
            continue;
          field.setAccessible(true);
          final Object value = field.get(o);
          if (value != null)
            map.put(field.getName(), value);
        }
        c = c.getSuperclass();
      }
      if (o instanceof DynamicObject)
        putAll(map, ((DynamicObject) o).fieldValues);
      return map;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public List<Map<String, Object>> objectToMap(Iterable l) {
    if (l == null)
      return null;
    List x = new ArrayList();
    for (Object o : l) x.add(objectToMap(o));
    return x;
  }

  static public double msToSeconds(long ms) {
    return toSeconds(ms);
  }

  static public double msToSeconds(double ms) {
    return toSeconds(ms);
  }

  static public long fileModificationTime(File f) {
    return f == null ? 0 : f.lastModified();
  }

  static public void closeRandomAccessFile(RandomAccessFile f) {
    if (f != null)
      try {
        f.close();
        callJavaX("dropIO", f);
      } catch (Throwable e) {
        printStackTrace(e);
      }
  }

  static public RandomAccessFile newRandomAccessFile(File path, String mode) {
    try {
      boolean forWrite = mode.indexOf('w') >= 0;
      if (forWrite)
        mkdirsForFile(path);
      RandomAccessFile f = new RandomAccessFile(path, mode);
      callJavaX("registerIO", f, path, forWrite);
      return f;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public String fsI_flex(String s) {
    return startsWithDigit(s) ? "#" + s : s;
  }

  static public DialogIO talkToSubBot(final long vport, final DialogIO io) {
    return talkToSubBot(String.valueOf(vport), io);
  }

  static public DialogIO talkToSubBot(final String subBot, final DialogIO io) {
    if (subBot == null)
      return io;
    return new talkToSubBot_IO(subBot, io);
  }

  static public class talkToSubBot_IO extends DialogIO {

    public String subBot;

    public DialogIO io;

    public talkToSubBot_IO(String subBot, DialogIO io) {
      this.io = io;
      this.subBot = subBot;
    }

    public boolean isStillConnected() {
      return io.isStillConnected();
    }

    public String readLineImpl() {
      return io.readLineImpl();
    }

    public boolean isLocalConnection() {
      return io.isLocalConnection();
    }

    public Socket getSocket() {
      return io.getSocket();
    }

    public void close() {
      try {
        io.close();
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public void sendLine(String line) {
      io.sendLine(format3("please forward to bot *: *", subBot, line));
    }
  }

  static public DialogIO talkTo(int port) {
    return talkTo("localhost", port);
  }

  static public int talkTo_defaultTimeout = 10000;

  static public int talkTo_timeoutForReads = 0;

  static public ThreadLocal<Map<String, DialogIO>> talkTo_byThread = new ThreadLocal();

  static public DialogIO talkTo(String ip, int port) {
    try {
      String full = ip + ":" + port;
      Map<String, DialogIO> map = talkTo_byThread.get();
      if (map != null && map.containsKey(full))
        return map.get(full);
      if (isLocalhost(ip) && port == vmPort())
        return talkToThisVM();
      return new talkTo_IO(ip, port);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public class talkTo_IO extends DialogIO {

    public String ip;

    public int port;

    public Socket s;

    public Writer w;

    public BufferedReader in;

    public talkTo_IO(String ip, int port) {
      this.port = port;
      this.ip = ip;
      try {
        s = new Socket();
        try {
          if (talkTo_timeoutForReads != 0)
            s.setSoTimeout(talkTo_timeoutForReads);
          s.connect(new InetSocketAddress(ip, port), talkTo_defaultTimeout);
        } catch (Throwable e) {
          throw fail("Tried talking to " + ip + ":" + port, e);
        }
        w = new OutputStreamWriter(s.getOutputStream(), "UTF-8");
        in = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public boolean isLocalConnection() {
      return s.getInetAddress().isLoopbackAddress();
    }

    public boolean isStillConnected() {
      return !(eos || s.isClosed());
    }

    public void sendLine(String line) {
      try {
        Lock __0 = lock;
        lock(__0);
        try {
          w.write(line + "\n");
          w.flush();
        } finally {
          unlock(__0);
        }
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public String readLineImpl() {
      try {
        return in.readLine();
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public void close() {
      try {
        if (!noClose)
          s.close();
      } catch (IOException e) {
      }
    }

    public Socket getSocket() {
      return s;
    }
  }

  static public int indexOfIgnoreCase(List<String> a, String b) {
    return indexOfIgnoreCase(a, b, 0);
  }

  static public int indexOfIgnoreCase(List<String> a, String b, int i) {
    int n = a == null ? 0 : a.size();
    for (; i < n; i++) if (eqic(a.get(i), b))
      return i;
    return -1;
  }

  static public int indexOfIgnoreCase(String[] a, String b) {
    return indexOfIgnoreCase(a, b, 0);
  }

  static public int indexOfIgnoreCase(String[] a, String b, int i) {
    int n = a == null ? 0 : a.length;
    for (; i < n; i++) if (eqic(a[i], b))
      return i;
    return -1;
  }

  static public int indexOfIgnoreCase(String a, String b) {
    return indexOfIgnoreCase_manual(a, b);
  }

  static public int indexOfIgnoreCase(String a, String b, int i) {
    return indexOfIgnoreCase_manual(a, b, i);
  }

  static public List<ProgramScan.Program> quickBotScan() {
    return ProgramScan.quickBotScan();
  }

  static public List<ProgramScan.Program> quickBotScan(int[] preferredPorts) {
    return ProgramScan.quickBotScan(preferredPorts);
  }

  static public List<ProgramScan.Program> quickBotScan(String searchPattern) {
    List<ProgramScan.Program> l = new ArrayList<ProgramScan.Program>();
    for (ProgramScan.Program p : ProgramScan.quickBotScan()) if (indexOfIgnoreCase(p.helloString, searchPattern) == 0)
      l.add(p);
    return l;
  }

  static public String firstPartOfHelloString(String s) {
    int i = s.lastIndexOf('/');
    return i < 0 ? s : rtrim(s.substring(0, i));
  }

  static public String sendToLocalBotQuietly(String bot, String text, Object... args) {
    text = format3(text, args);
    DialogIO channel = newFindBot2(bot);
    try {
      if (channel == null)
        throw fail(quote(bot) + " not found");
      try {
        channel.readLine();
        channel.sendLine(text);
        String s = channel.readLine();
        return s;
      } catch (Throwable e) {
        e.printStackTrace();
        return null;
      }
    } finally {
      _close(channel);
    }
  }

  static public String sendToLocalBotQuietly(int port, String text, Object... args) {
    text = format3(text, args);
    DialogIO channel = talkTo(port);
    try {
      try {
        channel.readLine();
        channel.sendLine(text);
        String s = channel.readLine();
        return s;
      } catch (Throwable e) {
        e.printStackTrace();
        return null;
      }
    } finally {
      _close(channel);
    }
  }

  static public Class hotwireOnce(String programID) {
    return hotwireCached(programID, false);
  }

  static public String getBotAddress(String bot) {
    List<ScannedBot> l = fullBotScan(bot);
    return empty(l) ? null : first(l).address;
  }

  static public Object unstructure_matchOK2OrFail(String s) {
    if (swic(s, "ok "))
      return unstructure_startingAtIndex(s, 3);
    else
      throw fail(s);
  }

  static public String sendToLocalBot(String bot, String text, Object... args) {
    text = format3(text, args);
    DialogIO channel = findBot(bot);
    try {
      if (channel == null)
        throw fail(quote(bot) + " not found");
      try {
        channel.readLine();
        print(bot + "> " + shorten(text, 80));
        channel.sendLine(text);
        String s = channel.readLine();
        print(bot + "< " + shorten(s, 80));
        return s;
      } catch (Throwable e) {
        e.printStackTrace();
        return null;
      }
    } finally {
      _close(channel);
    }
  }

  static public String sendToLocalBot(int port, String text, Object... args) {
    text = format3(text, args);
    DialogIO channel = talkTo(port);
    try {
      try {
        channel.readLine();
        print(port + "> " + shorten(text, 80));
        channel.sendLine(text);
        String s = channel.readLine();
        print(port + "< " + shorten(s, 80));
        return s;
      } catch (Throwable e) {
        e.printStackTrace();
        return null;
      }
    } finally {
      _close(channel);
    }
  }

  static public String format(String pat, Object... args) {
    return format3(pat, args);
  }

  static public <A extends Concept> A uniqueConcept(Class<A> c, Object... params) {
    return uniqueConcept(db_mainConcepts(), c, params);
  }

  static public <A extends Concept> A uniqueConcept(Concepts cc, Class<A> c, Object... params) {
    AutoCloseable __1 = tempDBLock(cc);
    try {
      params = expandParams(c, params);
      A x = findConceptWhere(cc, c, params);
      if (x == null) {
        x = unlisted(c);
        csetAll(x, params);
        cc.register(x);
      } else {
      }
      return x;
    } finally {
      _close(__1);
    }
  }

  static public String readLineHidden() {
    try {
      if (get(javax(), "readLine_reader") == null)
        set(javax(), "readLine_reader", new BufferedReader(new InputStreamReader(System.in, "UTF-8")));
      try {
        return ((BufferedReader) get(javax(), "readLine_reader")).readLine();
      } finally {
        consoleClearInput();
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public List<String> javaTokPlusPeriod(String s) {
    List<String> tok = new ArrayList<String>();
    if (s == null)
      return tok;
    int l = s.length();
    int i = 0;
    while (i < l) {
      int j = i;
      char c;
      String cc;
      while (j < l) {
        c = s.charAt(j);
        cc = s.substring(j, Math.min(j + 2, l));
        if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
          ++j;
        else if (cc.equals("/*")) {
          do ++j; while (j < l && !s.substring(j, Math.min(j + 2, l)).equals("*/"));
          j = Math.min(j + 2, l);
        } else if (cc.equals("//")) {
          do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0);
        } else
          break;
      }
      tok.add(s.substring(i, j));
      i = j;
      if (i >= l)
        break;
      c = s.charAt(i);
      cc = s.substring(i, Math.min(i + 2, l));
      if (c == (char) 0x201C || c == (char) 0x201D)
        c = '"';
      if (c == '\'' || c == '"') {
        char opener = c;
        ++j;
        while (j < l) {
          char _c = s.charAt(j);
          if (_c == (char) 0x201C || _c == (char) 0x201D)
            _c = '"';
          if (_c == opener) {
            ++j;
            break;
          } else if (s.charAt(j) == '\\' && j + 1 < l)
            j += 2;
          else
            ++j;
        }
        if (j - 1 >= i + 1) {
          tok.add(opener + s.substring(i + 1, j - 1) + opener);
          i = j;
          continue;
        }
      } else if (Character.isJavaIdentifierStart(c))
        do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || s.charAt(j) == '\''));
      else if (Character.isDigit(c))
        do ++j; while (j < l && Character.isDigit(s.charAt(j)));
      else if (cc.equals("[[")) {
        do ++j; while (j + 1 < l && !s.substring(j, j + 2).equals("]]"));
        j = Math.min(j + 2, l);
      } else if (cc.equals("[=") && i + 2 < l && s.charAt(i + 2) == '[') {
        do ++j; while (j + 2 < l && !s.substring(j, j + 3).equals("]=]"));
        j = Math.min(j + 3, l);
      } else if (s.substring(j, Math.min(j + 3, l)).equals("..."))
        j += 3;
      else if (c == '$' || c == '#')
        do ++j; while (j < l && Character.isDigit(s.charAt(j)));
      else
        ++j;
      tok.add(s.substring(i, j));
      i = j;
    }
    if ((tok.size() % 2) == 0)
      tok.add("");
    return tok;
  }

  static public boolean isNonNegativeInteger(String s) {
    int n = l(s);
    if (n == 0)
      return false;
    int i = 0;
    while (i < n) {
      char c = s.charAt(i);
      if (c < '0' || c > '9')
        return false;
      ++i;
    }
    return true;
  }

  static public void ensureDBNotRunning(String name) {
    if (hasBot(name)) {
      try {
        String framesBot = dropSuffix(".", name) + " Frames";
        print("Trying to activate frames of running DB: " + framesBot);
        if (isOK(sendOpt(framesBot, "activate frames")) && isMainProgram())
          cleanKill();
      } catch (Throwable __e) {
        pcallFail(__e);
      }
      throw fail("Already running: " + name);
    }
  }

  static public void ensureDBNotRunning() {
    ensureDBNotRunning(dbBotStandardName());
  }

  static public String dbBotStandardName() {
    String home = userHome();
    String name = dbBotName(getDBProgramID());
    if (neq(home, actualUserHome()))
      name += " " + quote(home);
    return name + ".";
  }

  static volatile public Android3 dbBot_instance;

  static public Android3 dbBot() {
    return dbBot(true);
  }

  static public Android3 dbBot(boolean ensureNotRunning) {
    return dbBot(dbBotStandardName(), ensureNotRunning);
  }

  static public Android3 dbBot(String name) {
    return dbBot(name, true);
  }

  static public Android3 dbBot(String name, boolean ensureNotRunning) {
    if (ensureNotRunning)
      ensureDBNotRunning(name);
    return dbBot_instance = methodsBot2(name, assertNotNull(db_mainConcepts()), db_standardExposedMethods(), db_mainConcepts().lock);
  }

  static public void thinAProgramsBackups(String progID, boolean doIt) {
    File dir = programDir(progID);
    thinAProgramsBackups(dir, doIt);
  }

  static public void thinAProgramsBackups(File dir, boolean doIt) {
    List<File> files = new ArrayList();
    Map<File, Double> ageMap = new HashMap();
    java.util.regex.Pattern pat = regexp("^(.*)\\.backup(20\\d\\d)(\\d\\d)(\\d\\d)-(\\d\\d)(\\d*)$");
    print("Processing backups in " + dir);
    for (File f : listFilesNotDirs(dir, newFile(dir, "backups"))) {
      String s = f.getName();
      java.util.regex.Matcher matcher = pat.matcher(s);
      {
        if (!(matcher.find()))
          continue;
      }
      String originalName = matcher.group(1);
      {
        if (!(eq(originalName, "concepts.structure.gz")))
          continue;
      }
      int year = matcherInt(matcher, 2);
      int month = matcherInt(matcher, 3);
      int day = matcherInt(matcher, 4);
      int hour = matcherInt(matcher, 5);
      int minute = matcherInt(matcher, 6);
      long time = timestampFromYMDHM(year, month, day, hour, minute);
      double age = ((now() - time) / 1000.0 / 60 / 60 / 24);
      ageMap.put(f, age);
      files.add(f);
    }
    int numDeleted = 0;
    sortByMap_inPlace(files, ageMap);
    double lastAge = -1;
    for (File f : files) {
      double age = ageMap.get(f);
      if (!thinAProgramsBackups_shouldKeep(age, lastAge)) {
        ++numDeleted;
        if (doIt) {
          print("Deleting: " + f);
          f.delete();
        }
      } else {
        lastAge = age;
      }
    }
    if (numDeleted != 0)
      print((doIt ? "Deleted: " : "Would delete: ") + n(numDeleted, "file"));
  }

  static public boolean thinAProgramsBackups_shouldKeep(double age, double lastAge) {
    return defaultAgeBasedBackupRetentionStrategy_shouldKeep(age, lastAge);
  }

  static public <A, B> List<B> lambdaMap(IF1<A, B> f, Iterable<A> l) {
    return map(l, f);
  }

  static public <A, B> List<B> lambdaMap(IF1<A, B> f, A[] l) {
    return map(l, f);
  }

  static public boolean containsIgnoreCase(Collection<String> l, String s) {
    if (l != null)
      for (String x : l) if (eqic(x, s))
        return true;
    return false;
  }

  static public boolean containsIgnoreCase(String[] l, String s) {
    if (l != null)
      for (String x : l) if (eqic(x, s))
        return true;
    return false;
  }

  static public boolean containsIgnoreCase(String s, char c) {
    return indexOfIgnoreCase(s, String.valueOf(c)) >= 0;
  }

  static public boolean containsIgnoreCase(String a, String b) {
    return indexOfIgnoreCase(a, b) >= 0;
  }

  static public int localYear() {
    return localYear(now());
  }

  static public int localYear(long time) {
    return parseInt(simpleDateFormat_local("yyyy").format(time));
  }

  static public long toLong(Object o) {
    if (o instanceof Number)
      return ((Number) o).longValue();
    if (o instanceof String)
      return parseLong((String) o);
    return 0;
  }

  static public long toLong(Timestamp ts) {
    return ts == null ? 0 : ts.unixDate();
  }

  static public long toLong(Date d) {
    return d == null ? 0 : d.getTime();
  }

  static public java.text.SimpleDateFormat simpleDateFormat(String format, TimeZone timeZone) {
    java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(format);
    sdf.setTimeZone(timeZone);
    return sdf;
  }

  static public int localMonth(long time) {
    return parseInt(simpleDateFormat_local("MM").format(time));
  }

  static public int localMonth() {
    return localMonth(now());
  }

  static public int localDayOfMonth(long time) {
    return parseInt(simpleDateFormat_local("dd").format(time));
  }

  static public int localDayOfMonth() {
    return localDayOfMonth(now());
  }

  static public String afterLastSpace(String s) {
    return s == null ? null : substring(s, s.lastIndexOf(' ') + 1);
  }

  static public AutoCloseable tempActivity(Object r) {
    return null;
  }

  static public long fixTimestamp(long timestamp) {
    return timestamp > now() ? 0 : timestamp;
  }

  static public String singleFieldName(Class c) {
    Set<String> l = listFields(c);
    if (l(l) != 1)
      throw fail("No single field found in " + c + " (have " + n(l(l), "fields") + ")");
    return first(l);
  }

  static public boolean isConceptList(Object o) {
    if (!(o instanceof List))
      return false;
    List l = (List) o;
    for (Object x : l) if (!(x instanceof Concept))
      return false;
    return true;
  }

  static public void dynamicObject_dropRawField(DynamicObject o, Object key) {
    if (o == null)
      return;
    synchronized (o) {
      o.fieldValues = (LinkedHashMap) syncMapRemove_deleteMapIfEmpty((Map) o.fieldValues, key);
    }
  }

  static public boolean isPersistable(Object o) {
    return !isInAnonymousClass(o);
  }

  static public <A, B> List<B> lmap(IF1<A, B> f, Iterable<A> l) {
    return lambdaMap(f, l);
  }

  static public <A, B> List<B> lmap(IF1<A, B> f, A[] l) {
    return lambdaMap(f, l);
  }

  static public String loadGZTextFile(File file) {
    try {
      if (!file.isFile())
        return null;
      ping();
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      InputStream fis = new FileInputStream(file);
      try {
        GZIPInputStream gis = newGZIPInputStream(fis);
        byte[] buffer = new byte[1024];
        int len;
        while ((len = gis.read(buffer)) != -1) baos.write(buffer, 0, len);
        baos.close();
        return fromUtf8(baos.toByteArray());
      } finally {
        _close(fis);
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Object getTL(Object o, String name) {
    return getThreadLocal(o, name);
  }

  static public <A> A getTL(ThreadLocal<A> tl) {
    return getThreadLocal(tl);
  }

  static public <A> A getTL(ThreadLocal<A> tl, A defaultValue) {
    return getThreadLocal(tl, defaultValue);
  }

  static public double toSeconds(long ms) {
    return ms / 1000.0;
  }

  static public String toSeconds(long ms, int digits) {
    return formatDouble(toSeconds(ms), digits);
  }

  static public double toSeconds(double ms) {
    return ms / 1000.0;
  }

  static public String toSeconds(double ms, int digits) {
    return formatDouble(toSeconds(ms), digits);
  }

  static public Object callJavaX(String method, Object... args) {
    return callOpt(getJavaX(), method, args);
  }

  static public boolean isLocalhost(String ip) {
    return isLoopbackIP(ip) || eqic(ip, "localhost");
  }

  static public int vmPort() {
    return myVMPort();
  }

  static public DialogIO talkToThisVM() {
    return new talkToThisVM_IO();
  }

  static public class talkToThisVM_IO extends DialogIO {

    public List<String> answers = ll(thisVMGreeting());

    public boolean isLocalConnection() {
      return true;
    }

    public boolean isStillConnected() {
      return true;
    }

    public int getPort() {
      return vmPort();
    }

    public void sendLine(String line) {
      answers.add(or2(sendToThisVM_newThread(line), "?"));
    }

    public String readLineImpl() {
      try {
        return popFirst(answers);
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public void close() {
    }

    public Socket getSocket() {
      return null;
    }
  }

  static public int indexOfIgnoreCase_manual(String a, String b) {
    return indexOfIgnoreCase_manual(a, b, 0);
  }

  static public int indexOfIgnoreCase_manual(String a, String b, int i) {
    int la = strL(a), lb = strL(b);
    if (la < lb)
      return -1;
    int n = la - lb;
    loop: for (; i <= n; i++) {
      for (int j = 0; j < lb; j++) {
        char c1 = a.charAt(i + j), c2 = b.charAt(j);
        if (!eqic(c1, c2))
          continue loop;
      }
      return i;
    }
    return -1;
  }

  public static String rtrim(String s) {
    if (s == null)
      return null;
    int i = s.length();
    while (i > 0 && " \t\r\n".indexOf(s.charAt(i - 1)) >= 0) --i;
    return i < s.length() ? s.substring(0, i) : s;
  }

  static public Map<String, Integer> newFindBot2_cache = synchroHashMap();

  static public boolean newFindBot2_verbose = false;

  static public DialogIO newFindBot2(String name) {
    Integer port = newFindBot2_cache.get(name);
    if (port != null) {
      if (newFindBot2_verbose)
        print("newFindBot2: testing " + name + " => " + port);
      DialogIO io = talkTo(port);
      String q = format("has bot *", name);
      String s = io.ask(q);
      if (match("yes", s)) {
        io = talkToSubBot(name, io);
        call(io, "pushback", "?");
        return io;
      }
      newFindBot2_cache.remove(name);
      if (newFindBot2_verbose)
        print("newFindBot2: dropping " + name + " => " + port);
    }
    DialogIO io = findBot(name);
    if (io != null) {
      newFindBot2_cache.put(name, io.getPort());
      if (newFindBot2_verbose)
        print("newFindBot2: remembering " + name + " => " + port);
    }
    return io;
  }

  static public TreeMap<String, Class> hotwireCached_cache = new TreeMap();

  static public Lock hotwireCached_lock = lock();

  static public Class hotwireCached(String programID) {
    return hotwireCached(programID, true);
  }

  static public Class hotwireCached(String programID, boolean runMain) {
    return hotwireCached(programID, runMain, false);
  }

  static public Class hotwireCached(String programID, boolean runMain, boolean dependent) {
    Lock __0 = hotwireCached_lock;
    lock(__0);
    try {
      programID = formatSnippetID(programID);
      Class c = hotwireCached_cache.get(programID);
      if (c == null) {
        c = hotwire(programID);
        if (dependent)
          makeDependent(c);
        if (runMain)
          callMain(c);
        hotwireCached_cache.put(programID, c);
      }
      return c;
    } finally {
      unlock(__0);
    }
  }

  static public class ScannedBot implements IFieldsToList {

    static final public String _fieldOrder = "helloString address";

    public String helloString;

    public String address;

    public ScannedBot() {
    }

    public ScannedBot(String helloString, String address) {
      this.address = address;
      this.helloString = helloString;
    }

    public String toString() {
      return shortClassName_dropNumberPrefix(this) + "(" + helloString + ", " + address + ")";
    }

    public boolean equals(Object o) {
      if (!(o instanceof ScannedBot))
        return false;
      ScannedBot __1 = (ScannedBot) o;
      return eq(helloString, __1.helloString) && eq(address, __1.address);
    }

    public int hashCode() {
      int h = 1660478935;
      h = boostHashCombine(h, _hashCode(helloString));
      h = boostHashCombine(h, _hashCode(address));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { helloString, address };
    }
  }

  static public List<ScannedBot> fullBotScan() {
    return fullBotScan("");
  }

  static public List<ScannedBot> fullBotScan(String searchPattern) {
    List<ScannedBot> bots = new ArrayList();
    for (ProgramScan.Program p : quickBotScan()) {
      String botName = firstPartOfHelloString(p.helloString);
      boolean isVM = startsWithIgnoreCase(p.helloString, "This is a JavaX VM.");
      boolean shouldRecurse = swic(botName, "Multi-Port") || isVM;
      if (swic(botName, searchPattern))
        bots.add(new ScannedBot(botName, "" + p.port));
      if (shouldRecurse)
        try {
          Map<Number, String> subBots = (Map) unstructure(sendToLocalBotQuietly(p.port, "list bots"));
          for (Number vport : subBots.keySet()) {
            botName = subBots.get(vport);
            if (swic(botName, searchPattern))
              bots.add(new ScannedBot(botName, p.port + "/" + vport));
          }
        } catch (Exception e) {
          e.printStackTrace();
        }
    }
    return bots;
  }

  static public Object unstructure_startingAtIndex(String s, int i) {
    return unstructure_tok(javaTokC_noMLS_iterator(s, i), false, null);
  }

  static public AutoCloseable tempDBLock(Concepts concepts) {
    return tempLock(concepts.lock);
  }

  static public AutoCloseable tempDBLock() {
    return tempDBLock(db_mainConcepts());
  }

  static public <A extends Concept> A unlisted(Class<A> c, Object... args) {
    concepts_unlisted.set(true);
    try {
      return nuObject(c, args);
    } finally {
      concepts_unlisted.set(null);
    }
  }

  static public Concept unlisted(String name, Object... args) {
    Class<? extends Concept> cc = findClass(name);
    concepts_unlisted.set(true);
    try {
      return cc != null ? nuObject(cc) : new Concept(name);
    } finally {
      concepts_unlisted.set(null);
    }
  }

  static public void consoleClearInput() {
    consoleSetInput("");
  }

  static public boolean hasBot(String searchPattern) {
    try {
      DialogIO io = findBot(searchPattern);
      if (io != null) {
        io.close();
        return true;
      } else
        return false;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public boolean isOK(String s) {
    s = trim(s);
    return swic(s, "ok ") || eqic(s, "ok") || matchStart("ok", s);
  }

  static public String sendOpt(String bot, String text, Object... args) {
    return sendToLocalBotOpt(bot, text, args);
  }

  static public boolean isMainProgram() {
    return creator() == null;
  }

  static public void cleanKill() {
    cleanKillVM();
  }

  static public Android3 methodsBot2(String name, final Object receiver, final List<String> exposedMethods) {
    return methodsBot2(name, receiver, exposedMethods, null);
  }

  static public Android3 methodsBot2(String name, final Object receiver, final List<String> exposedMethods, final Lock lock) {
    Android3 android = new Android3();
    android.greeting = name;
    android.console = false;
    android.responder = new Responder() {

      public String answer(String s, List<String> history) {
        return exposeMethods2(receiver, s, exposedMethods, lock);
      }
    };
    return makeBot(android);
  }

  static public List<String> db_standardExposedMethods_list = ll("xlist", "xnew", "xset", "xdelete", "xget", "xclass", "xfullgrab", "xshutdown", "xchangeCount", "xcount");

  static public List<String> db_standardExposedMethods() {
    return db_standardExposedMethods_list;
  }

  static public Matcher regexp(String pat, String s) {
    return regexp(compileRegexp(pat), unnull(s));
  }

  static public Matcher regexp(java.util.regex.Pattern pat, String s) {
    return pat.matcher(unnull(s));
  }

  static public java.util.regex.Pattern regexp(String pat) {
    return compileRegexp(pat);
  }

  static public List<File> listFilesNotDirs(String dir) {
    return listFilesOnly(dir);
  }

  static public List<File> listFilesNotDirs(File... dirs) {
    return listFilesOnly(dirs);
  }

  static public int matcherInt(Matcher m, int i) {
    return parseInt(m.group(i));
  }

  static public long timestampFromYMDHM(int y, int m, int d, int h, int minutes) {
    return new GregorianCalendar(y, m - 1, d, h, minutes).getTimeInMillis();
  }

  static public <A> List<A> sortByMap_inPlace(List<A> l, Map<A, ?> map) {
    sort(l, mapComparator(map));
    return l;
  }

  static public boolean defaultAgeBasedBackupRetentionStrategy_shouldKeep(double age, double lastAge) {
    if (age <= 1 / 12.0)
      return true;
    if (age <= 0.5 && age >= lastAge + 1 / 12.0)
      return true;
    if (age <= 7 && age >= lastAge + 1)
      return true;
    if (age <= 28 && age >= lastAge + 7)
      return true;
    if (age >= lastAge + 365.0 / 12)
      return true;
    return false;
  }

  static public Set<String> listFields(Object c) {
    TreeSet<String> fields = new TreeSet();
    for (Field f : _getClass(c).getDeclaredFields()) fields.add(f.getName());
    return fields;
  }

  static public <A, B, C extends Map<A, B>> C syncMapRemove_deleteMapIfEmpty(C map, A key) {
    if (map != null && key != null)
      synchronized (collectionMutex(map)) {
        map.remove(key);
        if (map.isEmpty())
          return null;
      }
    return map;
  }

  static public boolean isInAnonymousClass(Object o) {
    if (o == null)
      return false;
    return isAnonymousClassName(className(o));
  }

  static public String fromUtf8(byte[] bytes) {
    try {
      return bytes == null ? null : new String(bytes, utf8charset());
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public boolean isLoopbackIP(String ip) {
    return eq(ip, "127.0.0.1");
  }

  static public int myVMPort() {
    List records = (List) (get(getJavaX(), "record_list"));
    Object android = last(records);
    return or0((Integer) get(android, "port"));
  }

  static public String thisVMGreeting() {
    List record_list = (List) (get(getJavaX(), "record_list"));
    Object android = first(record_list);
    return getString(android, "greeting");
  }

  static public String sendToThisVM_newThread(String s, Object... args) {
    final String _s = format(s, args);
    try {
      return (String) evalInNewThread(new F0<Object>() {

        public Object get() {
          try {
            return callStaticAnswerMethod(getJavaX(), _s);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "return callStaticAnswerMethod(getJavaX(), _s);";
        }
      });
    } catch (Throwable e) {
      e = getInnerException(e);
      printStackTrace(e);
      return str(e);
    }
  }

  static public boolean match(String pat, String s) {
    return match3(pat, s);
  }

  static public boolean match(String pat, String s, Matches matches) {
    return match3(pat, s, matches);
  }

  static public boolean match(String pat, List<String> toks, Matches matches) {
    return match3(pat, toks, matches);
  }

  static public Object makeDependent_postProcess;

  static public void makeDependent(Object c) {
    if (c == null)
      return;
    assertTrue("Not a class", c instanceof Class);
    dependentClasses();
    hotwire_classes.add(new WeakReference(c));
    Object local_log = getOpt(mc(), "local_log");
    if (local_log != null)
      setOpt(c, "local_log", local_log);
    Object print_byThread = getOpt(mc(), "print_byThread");
    if (print_byThread != null)
      setOpt(c, "print_byThread", print_byThread);
    callF(makeDependent_postProcess, c);
  }

  static public AutoCloseable tempLock(Lock lock) {
    return tempLock("", lock);
  }

  static public AutoCloseable tempLock(String purpose, Lock lock) {
    if (lock == null)
      return null;
    lock(lock);
    return new AutoCloseable() {

      public String toString() {
        return "unlock(lock);";
      }

      public void close() throws Exception {
        unlock(lock);
      }
    };
  }

  static public void consoleSetInput(final String text) {
    if (headless())
      return;
    setTextAndSelectAll(consoleInputField(), text);
    focusConsole();
  }

  static public boolean matchStart(String pat, String s) {
    return matchStart(pat, s, null);
  }

  static public boolean matchStart(String pat, String s, Matches matches) {
    if (s == null)
      return false;
    return matchStart(pat, parse3_cachedInput(s), matches);
  }

  static public boolean matchStart(String pat, List<String> toks, Matches matches) {
    if (toks == null)
      return false;
    List<String> tokpat = parse3_cachedPattern(pat);
    if (toks.size() < tokpat.size())
      return false;
    String[] m = match2(tokpat, toks.subList(0, tokpat.size()));
    if (m == null)
      return false;
    if (matches != null) {
      matches.m = new String[m.length + 1];
      arraycopy(m, matches.m);
      matches.m[m.length] = joinSubList(toks, tokpat.size(), toks.size());
    }
    return true;
  }

  static public String sendToLocalBotOpt(String bot, String text, Object... args) {
    if (bot == null)
      return null;
    text = format(text, args);
    DialogIO channel = findBot(bot);
    try {
      if (channel == null) {
        print(quote(bot) + " not found, skipping send: " + quote(text));
        return null;
      }
      try {
        channel.readLine();
        print(shorten(bot + "> " + text, 200));
        channel.sendLine(text);
        String s = channel.readLine();
        print(shorten(bot + "< " + s, 200));
        return s;
      } catch (Throwable e) {
        e.printStackTrace();
        return null;
      }
    } finally {
      _close(channel);
    }
  }

  static public WeakReference<Object> creator_class;

  static public Object creator() {
    return creator_class == null ? null : creator_class.get();
  }

  static public boolean exposeMethods2_debug = false;

  static public String exposeMethods2(Object receiver, String s, List<String> methodNames) {
    return exposeMethods2(receiver, s, methodNames, null);
  }

  static public String exposeMethods2(Object receiver, String s, List<String> methodNames, Lock lock) {
    Matches m = new Matches();
    if (exposeMethods2_debug)
      print("Received: " + s);
    if (match("call *", s, m)) {
      List l;
      if (isIdentifier(m.unq(0)))
        l = ll(m.unq(0));
      else
        l = (List) unstructure(m.unq(0));
      String method = getString(l, 0);
      if (!contains(methodNames, method))
        throw fail("Method not allowed: " + method);
      if (lock != null)
        lock.lock();
      try {
        if (exposeMethods2_debug)
          print("Calling: " + method);
        Object o = call(receiver, method, asObjectArray(subList(l, 1)));
        if (exposeMethods2_debug)
          print("Got: " + getClassName(o));
        return ok2(structure(o));
      } finally {
        if (lock != null)
          lock.unlock();
      }
    }
    if (match("list methods", s))
      return ok2(structure(methodNames));
    return null;
  }

  static public int makeBot(String greeting) {
    return makeAndroid3(greeting).port;
  }

  static public Android3 makeBot(Android3 a) {
    makeAndroid3(a);
    return a;
  }

  static public Android3 makeBot(String greeting, Object responder) {
    Android3 a = new Android3(greeting);
    a.responder = makeResponder(responder);
    makeBot(a);
    return a;
  }

  static public Android3 makeBot() {
    return makeAndroid3(defaultBotName());
  }

  static public Map<String, java.util.regex.Pattern> compileRegexp_cache = syncMRUCache(10);

  static public java.util.regex.Pattern compileRegexp(String pat) {
    java.util.regex.Pattern p = compileRegexp_cache.get(pat);
    if (p == null) {
      compileRegexp_cache.put(pat, p = java.util.regex.Pattern.compile(pat));
    }
    return p;
  }

  static public List<File> listFilesOnly(String dir) {
    return listFilesOnly(new File(dir));
  }

  static public List<File> listFilesOnly(File... dirs) {
    return concatMap(dir -> listFilesWithSuffix("", dir), dirs);
  }

  static public <A, B> Comparator<A> mapComparator(final Map<A, B> map) {
    return new Comparator<A>() {

      public int compare(A a, A b) {
        return cmp(map.get(a), map.get(b));
      }
    };
  }

  static public int or0(Integer i) {
    return i == null ? 0 : i;
  }

  static public long or0(Long l) {
    return l == null ? 0L : l;
  }

  static public double or0(Double d) {
    return d == null ? 0.0 : d;
  }

  static public String getString(Map map, Object key) {
    return map == null ? null : (String) map.get(key);
  }

  static public String getString(List l, int idx) {
    return (String) get(l, idx);
  }

  static public String getString(Object o, Object key) {
    if (o instanceof Map)
      return getString((Map) o, key);
    if (key instanceof String)
      return (String) getOpt(o, (String) key);
    throw fail("Not a string key: " + getClassName(key));
  }

  static public String getString(String key, Object o) {
    return getString(o, (Object) key);
  }

  static public Object evalInNewThread(final Object f) {
    final Flag flag = new Flag();
    final Var var = new Var();
    final Var<Throwable> exception = new Var();
    {
      startThread(new Runnable() {

        public void run() {
          try {
            try {
              var.set(callF(f));
            } catch (Throwable e) {
              exception.set(e);
            }
            flag.raise();
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "try {\r\n      var.set(callF(f));\r\n    } catch (Throwable e) {\r\n      exception...";
        }
      });
    }
    flag.waitUntilUp();
    if (exception.has())
      throw rethrow(exception.get());
    return var.get();
  }

  static public String callStaticAnswerMethod(List bots, String s) {
    for (Object c : bots) try {
      String answer = callStaticAnswerMethod(c, s);
      if (!empty(answer))
        return answer;
    } catch (Throwable e) {
      print("Error calling " + getProgramID(c));
      e.printStackTrace();
    }
    return null;
  }

  static public String callStaticAnswerMethod(Object c, String s) {
    String answer = (String) callOpt(c, "answer", s, litlist(s));
    if (answer == null)
      answer = (String) callOpt(c, "answer", s);
    return emptyToNull(answer);
  }

  static public String callStaticAnswerMethod(String s) {
    return callStaticAnswerMethod(mc(), s);
  }

  static public String callStaticAnswerMethod(String s, List<String> history) {
    return callStaticAnswerMethod(mc(), s, history);
  }

  static public String callStaticAnswerMethod(Object c, String s, List<String> history) {
    String answer = (String) callOpt(c, "answer", s, history);
    if (answer == null)
      answer = (String) callOpt(c, "answer", s);
    return emptyToNull(answer);
  }

  static public boolean match3(String pat, String s) {
    return match3(pat, s, null);
  }

  static public boolean match3(String pat, String s, Matches matches) {
    if (pat == null || s == null)
      return false;
    return match3(pat, parse3_cachedInput(s), matches);
  }

  static public boolean match3(String pat, List<String> toks, Matches matches) {
    List<String> tokpat = parse3_cachedPattern(pat);
    return match3(tokpat, toks, matches);
  }

  static public boolean match3(List<String> tokpat, List<String> toks, Matches matches) {
    String[] m = match2(tokpat, toks);
    if (m == null)
      return false;
    if (matches != null)
      matches.m = m;
    return true;
  }

  static public List<Class> dependentClasses() {
    return cleanUpAndGetWeakReferencesList(hotwire_classes);
  }

  static public boolean headless() {
    return isHeadless();
  }

  static public JTextField setTextAndSelectAll(final JTextField tf, final String text) {
    if (tf != null) {
      swing(() -> {
        tf.setText(text);
        tf.selectAll();
      });
    }
    return tf;
  }

  static public JTextField consoleInputField() {
    Object console = get(getJavaX(), "console");
    return (JTextField) getOpt(console, "tfInput");
  }

  static public void focusConsole(String s) {
    setConsoleInput(s);
    focusConsole();
  }

  static public void focusConsole() {
    JComponent tf = consoleInputFieldOrComboBox();
    if (tf != null) {
      tf.requestFocus();
    }
  }

  static public List<WeakReference<Class>> hotwire_classes = synchroList();

  static public Class<?> hotwireDependent(String src) {
    Class c = hotwire(src);
    makeDependent(c);
    return c;
  }

  static public Map<String, List<String>> parse3_cachedInput_cache = synchronizedMRUCache(1000);

  static public List<String> parse3_cachedInput(String s) {
    List<String> tok = parse3_cachedInput_cache.get(s);
    if (tok == null)
      parse3_cachedInput_cache.put(s, tok = parse3(s));
    return tok;
  }

  static public Map<String, List<String>> parse3_cachedPattern_cache = synchronizedMRUCache(1000);

  static synchronized public List<String> parse3_cachedPattern(String s) {
    List<String> tok = parse3_cachedPattern_cache.get(s);
    if (tok == null)
      parse3_cachedPattern_cache.put(s, tok = parse3(s));
    return tok;
  }

  static public Object[] asObjectArray(Collection l) {
    return toObjectArray(l);
  }

  static public String ok2(String s) {
    return "ok " + s;
  }

  static public boolean makeAndroid3_disable = false;

  static public class Android3 implements AutoCloseable {

    public String greeting;

    public boolean publicOverride = false;

    public int startPort = 5000;

    public IResponder responder;

    public boolean console = true;

    public boolean quiet = false;

    public boolean daemon = false;

    public boolean incomingSilent = false;

    public int incomingPrintLimit = 200;

    public boolean useMultiPort = true;

    public boolean recordHistory = false;

    public boolean verbose = false;

    public int answerPrintLimit = 500;

    public boolean newLineAboveAnswer, newLineBelowAnswer;

    public int port;

    public long vport;

    public IDialogHandler handler;

    public ServerSocket server;

    public Android3(String greeting) {
      this.greeting = greeting;
    }

    public Android3() {
    }

    public void close() {
      dispose();
    }

    synchronized public void dispose() {
      if (server != null) {
        try {
          server.close();
        } catch (IOException e) {
          print("[internal] " + e);
        }
        server = null;
      }
      if (vport != 0) {
        try {
          print("Disposing " + this);
          removeFromMultiPort(vport);
          vport = 0;
        } catch (Throwable __e) {
          pcallFail(__e);
        }
      }
    }

    public String toString() {
      return "Bot: " + greeting + " [port " + port + (vport == 0 ? "" : ", vport " + vport) + "]";
    }
  }

  static abstract public class Responder implements IResponder {
  }

  public interface IResponder {

    public String answer(String s, List<String> history);
  }

  static public Android3 makeAndroid3(final String greeting) {
    return makeAndroid3(new Android3(greeting));
  }

  static public Android3 makeAndroid3(final String greeting, Responder responder) {
    Android3 android = new Android3(greeting);
    android.responder = responder;
    return makeAndroid3(android);
  }

  static public Android3 makeAndroid3(final Android3 a) {
    if (makeAndroid3_disable)
      return a;
    if (a.responder == null)
      a.responder = new Responder() {

        public String answer(String s, List<String> history) {
          return callStaticAnswerMethod(s, history);
        }
      };
    if (!a.quiet)
      print("[bot] " + a.greeting);
    if (a.console && (readLine_noReadLine || makeAndroid3_consoleInUse()))
      a.console = false;
    record(a);
    if (a.useMultiPort)
      a.vport = addToMultiPort(a.greeting, makeAndroid3_verboseResponder(a));
    if (a.console)
      makeAndroid3_handleConsole(a);
    if (a.useMultiPort)
      return a;
    a.handler = makeAndroid3_makeDialogHandler(a);
    if (a.quiet)
      startDialogServer_quiet.set(true);
    try {
      a.port = a.daemon ? startDialogServerOnPortAboveDaemon(a.startPort, a.handler) : startDialogServerOnPortAbove(a.startPort, a.handler);
    } finally {
      startDialogServer_quiet.set(null);
    }
    a.server = startDialogServer_serverSocket;
    return a;
  }

  static public void makeAndroid3_handleConsole(final Android3 a) {
    if (!a.quiet)
      print("You may also type on this console.");
    {
      startThread(new Runnable() {

        public void run() {
          try {
            List<String> history = new ArrayList();
            while (licensed()) {
              String line;
              try {
                line = readLine();
              } catch (Throwable e) {
                print(getInnerMessage(e));
                break;
              }
              if (line == null)
                break;
              {
                history.add(line);
                history.add(makeAndroid3_getAnswer(line, history, a));
              }
            }
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "List<String> history = new ArrayList();\r\n    while (licensed()) {\r\n      Stri...";
        }
      });
    }
  }

  static public DialogHandler makeAndroid3_makeDialogHandler(final Android3 a) {
    return new DialogHandler() {

      public void run(final DialogIO io) {
        if (!a.publicOverride && !(publicCommOn() || io.isLocalConnection())) {
          io.sendLine("Sorry, not allowed");
          return;
        }
        String dialogID = randomID(8);
        io.sendLine(a.greeting + " / Your ID: " + dialogID);
        List<String> history = new ArrayList();
        while (io.isStillConnected()) {
          if (io.waitForLine()) {
            final String line = io.readLineNoBlock();
            String s = dialogID + " at " + now() + ": " + quote(line);
            if (!a.incomingSilent)
              print(shorten(s, a.incomingPrintLimit));
            if (eq(line, "bye")) {
              io.sendLine("bye stranger");
              return;
            }
            Matches m = new Matches();
            if (a.recordHistory)
              history.add(line);
            String answer;
            if (match3("this is a continuation of talk *", s, m) || match3("hello bot! this is a continuation of talk *", s, m)) {
              dialogID = unquote(m.m[0]);
              answer = "ok";
            } else
              try {
                makeAndroid3_io.set(io);
                answer = makeAndroid3_getAnswer(line, history, a);
              } finally {
                makeAndroid3_io.set(null);
              }
            if (a.recordHistory)
              history.add(answer);
            io.sendLine(answer);
          }
        }
      }
    };
  }

  static public String makeAndroid3_getAnswer(String line, List<String> history, Android3 a) {
    String answer, originalAnswer;
    try {
      originalAnswer = a.responder.answer(line, history);
      answer = makeAndroid3_fallback(line, history, originalAnswer);
    } catch (Throwable e) {
      e = getInnerException(e);
      printStackTrace(e);
      originalAnswer = answer = e.toString();
    }
    if (!a.incomingSilent) {
      if (originalAnswer == null)
        originalAnswer = "?";
      if (a.newLineAboveAnswer)
        print();
      print(">" + dropFirst(indentx(2, shorten(rtrim(originalAnswer), a.answerPrintLimit))));
      if (a.newLineBelowAnswer)
        print();
    }
    return answer;
  }

  static public String makeAndroid3_fallback(String s, List<String> history, String answer) {
    if (answer == null && match3("what is your pid", s))
      return getPID();
    if (answer == null && match3("what is your program id", s))
      return getProgramID();
    if (match3("get injection id", s))
      return getInjectionID();
    if (answer == null)
      answer = "?";
    if (answer.indexOf('\n') >= 0 || answer.indexOf('\r') >= 0)
      answer = quote(answer);
    return answer;
  }

  static public boolean makeAndroid3_consoleInUse() {
    if (isTrue(vm_generalMap_get("consoleInUse")))
      return true;
    for (Object o : record_list) if (o instanceof Android3 && ((Android3) o).console)
      return true;
    return false;
  }

  static public Responder makeAndroid3_verboseResponder(final Android3 a) {
    return new Responder() {

      public String answer(String s, List<String> history) {
        if (a.verbose)
          print("> " + shorten(s, a.incomingPrintLimit));
        String answer = a.responder.answer(s, history);
        if (a.verbose)
          print("< " + shorten(answer, a.incomingPrintLimit));
        return answer;
      }
    };
  }

  static public ThreadLocal<DialogIO> makeAndroid3_io = new ThreadLocal();

  static public Android3 makeAndroid3() {
    return makeAndroid3(getProgramTitle() + ".");
  }

  static public String makeResponder_callAnswerMethod(Object bot, String s, List<String> history) {
    String answer = (String) callOpt(bot, "answer", s, history);
    if (answer == null)
      answer = (String) callOpt(bot, "answer", s);
    return answer;
  }

  static public Responder makeResponder(final Object bot) {
    if (bot instanceof Responder)
      return (Responder) bot;
    if (bot instanceof String) {
      String f = (String) bot;
      return new Responder() {

        public String answer(String s, List<String> history) {
          String answer = (String) callOptMC((String) bot, s, history);
          if (answer == null)
            answer = (String) callOptMC((String) bot, s);
          return answer;
        }
      };
    }
    return new Responder() {

      public String answer(String s, List<String> history) {
        return makeResponder_callAnswerMethod(bot, s, history);
      }
    };
  }

  static public String defaultBotName() {
    return getProgramTitle() + ".";
  }

  static public <A, B> Map<A, B> syncMRUCache(int size) {
    return synchroMap(new MRUCache(size));
  }

  static public List<File> listFilesWithSuffix(File dir, String suffix) {
    List<File> l = new ArrayList();
    for (File f : listFiles(dir)) if (!f.isDirectory() && (empty(suffix) || endsWithIgnoreCase(f.getName(), suffix)))
      l.add(f);
    return l;
  }

  static public List<File> listFilesWithSuffix(String suffix, File dir) {
    return listFilesWithSuffix(dir, suffix);
  }

  static public String emptyToNull(String s) {
    return eq(s, "") ? null : s;
  }

  static public <A, B> Map<A, B> emptyToNull(Map<A, B> map) {
    return empty(map) ? null : map;
  }

  static public <A> List<A> cleanUpAndGetWeakReferencesList(List<WeakReference<A>> l) {
    if (l == null)
      return null;
    synchronized (l) {
      List<A> out = new ArrayList();
      for (int i = 0; i < l(l); i++) {
        A a = l.get(i).get();
        if (a == null)
          l.remove(i--);
        else
          out.add(a);
      }
      return out;
    }
  }

  static public void setConsoleInput(String text) {
    consoleSetInput(text);
  }

  static public JComponent consoleInputFieldOrComboBox() {
    Object console = get(getJavaX(), "console");
    JComboBox cb = (JComboBox) (getOpt(console, "cbInput"));
    if (cb != null)
      return cb;
    return (JTextField) getOpt(console, "tfInput");
  }

  static public List<String> parse3(String s) {
    return dropPunctuation(javaTokPlusPeriod(s));
  }

  static public void removeFromMultiPort(long vport) {
    if (vport == 0)
      return;
    for (Object port : getMultiPorts()) call(port, "removePort", vport);
  }

  static public List<Object> record_list = synchroList();

  static public void record(Object o) {
    record_list.add(o);
  }

  static public Object addToMultiPort_responder;

  static public long addToMultiPort(final String botName) {
    return addToMultiPort(botName, new Object() {

      public String answer(String s, List<String> history) {
        String answer = (String) (callOpt(getMainClass(), "answer", s, history));
        if (answer != null)
          return answer;
        answer = (String) callOpt(getMainClass(), "answer", s);
        if (answer != null)
          return answer;
        if (match3("get injection id", s))
          return getInjectionID();
        return null;
      }
    });
  }

  static public long addToMultiPort(final String botName, final Object responder) {
    addToMultiPort_responder = responder;
    startMultiPort();
    List ports = getMultiPorts();
    if (ports == null)
      return 0;
    if (ports.isEmpty())
      throw fail("No multiports!");
    if (ports.size() > 1)
      print("Multiple multi-ports. Using last one.");
    Object port = last(ports);
    Object responder2 = new Object() {

      public String answer(String s, List<String> history) {
        if (match3("get injection id", s))
          return getInjectionID();
        if (match3("your name", s))
          return botName;
        return (String) call(responder, "answer", s, history);
      }
    };
    record(responder2);
    return (Long) call(port, "addResponder", botName, responder2);
  }

  static public AtomicInteger dialogServer_clients = new AtomicInteger();

  static public boolean dialogServer_printConnects = false;

  static public ThreadLocal<Boolean> startDialogServer_quiet = new ThreadLocal();

  static public Set<String> dialogServer_knownClients = synchroTreeSet();

  static public int startDialogServerOnPortAbove(int port, IDialogHandler handler) {
    while (!forbiddenPort(port) && !startDialogServerIfPortAvailable(port, handler)) ++port;
    return port;
  }

  static public int startDialogServerOnPortAboveDaemon(int port, IDialogHandler handler) {
    while (!forbiddenPort(port) && !startDialogServerIfPortAvailable(port, handler, true)) ++port;
    return port;
  }

  static public void startDialogServer(int port, IDialogHandler handler) {
    if (!startDialogServerIfPortAvailable(port, handler))
      throw fail("Can't start dialog server on port " + port);
  }

  static public boolean startDialogServerIfPortAvailable(int port, final IDialogHandler handler) {
    return startDialogServerIfPortAvailable(port, handler, false);
  }

  static public ServerSocket startDialogServer_serverSocket;

  static public boolean startDialogServerIfPortAvailable(int port, final IDialogHandler handler, boolean daemon) {
    ServerSocket serverSocket = null;
    try {
      serverSocket = new ServerSocket(port);
    } catch (IOException e) {
      return false;
    }
    final ServerSocket _serverSocket = serverSocket;
    startDialogServer_serverSocket = serverSocket;
    Thread thread = new Thread("Socket accept port " + port) {

      public void run() {
        try {
          while (true) {
            try {
              final Socket s = _serverSocket.accept();
              String client = s.getInetAddress().toString();
              if (!dialogServer_knownClients.contains(client) && neq(client, "/127.0.0.1")) {
                print("connect from " + client + " - clients: " + dialogServer_clients.incrementAndGet());
                dialogServer_knownClients.add(client);
              }
              String threadName = "Handling client " + s.getInetAddress();
              Thread t2 = new Thread(threadName) {

                public void run() {
                  try {
                    final Writer w = new OutputStreamWriter(s.getOutputStream(), "UTF-8");
                    final BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
                    DialogIO io = new DialogIO() {

                      public boolean isLocalConnection() {
                        return s.getInetAddress().isLoopbackAddress();
                      }

                      public boolean isStillConnected() {
                        return !(eos || s.isClosed());
                      }

                      public void sendLine(String line) {
                        try {
                          w.write(line + "\n");
                          w.flush();
                        } catch (Exception __e) {
                          throw rethrow(__e);
                        }
                      }

                      public String readLineImpl() {
                        try {
                          return in.readLine();
                        } catch (Exception __e) {
                          throw rethrow(__e);
                        }
                      }

                      public void close() {
                        try {
                          s.close();
                        } catch (IOException e) {
                        }
                      }

                      public Socket getSocket() {
                        return s;
                      }
                    };
                    try {
                      handler.run(io);
                    } finally {
                      if (!io.noClose)
                        s.close();
                    }
                  } catch (IOException e) {
                    print("[internal] " + e);
                  } finally {
                  }
                }
              };
              t2.setDaemon(true);
              t2.start();
            } catch (SocketTimeoutException e) {
            }
          }
        } catch (IOException e) {
          print("[internal] " + e);
        }
      }
    };
    if (daemon)
      thread.setDaemon(true);
    thread.start();
    if (!isTrue(getAndClearThreadLocal(startDialogServer_quiet)))
      print("Dialog server on port " + port + " started.");
    return true;
  }

  static public boolean publicCommOn() {
    return "1".equals(loadTextFile(new File(userHome(), ".javax/public-communication")));
  }

  static public int randomID_defaultLength = 12;

  static public String randomID(int length) {
    return makeRandomID(length);
  }

  static public String randomID(Random r, int length) {
    return makeRandomID(r, length);
  }

  static public String randomID() {
    return randomID(randomID_defaultLength);
  }

  static public String randomID(Random r) {
    return randomID(r, randomID_defaultLength);
  }

  static public String processID_cached;

  static public String getPID() {
    if (processID_cached == null) {
      String name = ManagementFactory.getRuntimeMXBean().getName();
      processID_cached = name.replaceAll("@.*", "");
    }
    return processID_cached;
  }

  static public String getInjectionID() {
    return (String) call(getJavaX(), "getInjectionID", getMainClass());
  }

  static public String getProgramTitle() {
    return getProgramName();
  }

  static public Object callOptMC(String method, Object... args) {
    return callOpt(mc(), method, args);
  }

  static public List<String> dropPunctuation_keep = ll("*", "<", ">");

  static public List<String> dropPunctuation(List<String> tok) {
    tok = new ArrayList<String>(tok);
    for (int i = 1; i < tok.size(); i += 2) {
      String t = tok.get(i);
      if (t.length() == 1 && !Character.isLetter(t.charAt(0)) && !Character.isDigit(t.charAt(0)) && !dropPunctuation_keep.contains(t)) {
        tok.set(i - 1, tok.get(i - 1) + tok.get(i + 1));
        tok.remove(i);
        tok.remove(i);
        i -= 2;
      }
    }
    return tok;
  }

  static public String dropPunctuation(String s) {
    return join(dropPunctuation(nlTok(s)));
  }

  static public List<Object> getMultiPorts() {
    return (List) callOpt(getJavaX(), "getMultiPorts");
  }

  static public void startMultiPort() {
    List mp = getMultiPorts();
    if (mp != null && mp.isEmpty()) {
      nohupJavax("#1001639");
      throw fail("Upgrading JavaX, please restart this program afterwards.");
    }
  }

  static public <A> Set<A> synchroTreeSet() {
    return Collections.synchronizedSet(new TreeSet<A>());
  }

  static public <A> Set<A> synchroTreeSet(TreeSet<A> set) {
    return Collections.synchronizedSet(set);
  }

  static public boolean forbiddenPort(int port) {
    return port == 5037;
  }

  static public String makeRandomID(int length) {
    return makeRandomID(length, defaultRandomGenerator());
  }

  static public String makeRandomID(int length, Random random) {
    char[] id = new char[length];
    for (int i = 0; i < id.length; i++) id[i] = (char) ((int) 'a' + random.nextInt(26));
    return new String(id);
  }

  static public String makeRandomID(Random r, int length) {
    return makeRandomID(length, r);
  }

  static public String getProgramName_cache;

  static public String getProgramName() {
    Lock __0 = downloadLock();
    lock(__0);
    try {
      if (getProgramName_cache == null)
        getProgramName_cache = getSnippetTitleOpt(programID());
      return getProgramName_cache;
    } finally {
      unlock(__0);
    }
  }

  static public void _onLoad_getProgramName() {
    {
      startThread(new Runnable() {

        public void run() {
          try {
            getProgramName();
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "getProgramName();";
        }
      });
    }
  }

  static public List<String> nlTok(String s) {
    return javaTokPlusPeriod(s);
  }

  static public Lock downloadLock_lock = fairLock();

  static public Lock downloadLock() {
    return downloadLock_lock;
  }

  static public String getSnippetTitleOpt(String s) {
    try {
      return isSnippetID(s) ? getSnippetTitle(s) : s;
    } catch (Throwable __e) {
      pcallFail(__e);
    }
    return s;
  }

  static public String getSnippetTitle(String id) {
    if (id == null)
      return null;
    if (!isSnippetID(id))
      return "?";
    IResourceLoader rl = vm_getResourceLoader();
    if (rl != null)
      return rl.getSnippetTitle(id);
    return getSnippetTitle_noResourceLoader(id);
  }

  static public String getSnippetTitle_noResourceLoader(String id) {
    try {
      if (isLocalSnippetID(id))
        return localSnippetTitle(id);
      long parsedID = parseSnippetID(id);
      String url;
      if (isImageServerSnippet(parsedID))
        url = imageServerURL() + "title/" + parsedID + muricaCredentialsQuery();
      else if (isGeneralFileServerSnippet(parsedID))
        url = "http://butter.botcompany.de:8080/files/name/" + parsedID;
      else
        url = tb_mainServer() + "/tb-int/getfield.php?id=" + parsedID + "&field=title" + standardCredentials_noCookies();
      String title = trim(loadPageSilently(url));
      if (title != null)
        try {
          saveTextFileIfChanged(snippetTitle_cacheFile(id), title);
        } catch (Throwable __e) {
          print(exceptionToStringShort(__e));
        }
      return or(title, "?");
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public String getSnippetTitle(long id) {
    return getSnippetTitle(fsI(id));
  }

  static public IResourceLoader vm_getResourceLoader() {
    return proxy(IResourceLoader.class, vm_generalMap_get("_officialResourceLoader"));
  }

  static public boolean isLocalSnippetID(String snippetID) {
    return isSnippetID(snippetID) && isLocalSnippetID(psI(snippetID));
  }

  static public boolean isLocalSnippetID(long snippetID) {
    return snippetID >= 1000 && snippetID <= 9999;
  }

  static public String localSnippetTitle(String snippetID) {
    if (!isLocalSnippetID(snippetID))
      return null;
    File f = localSnippetFile(snippetID);
    if (!f.exists())
      return null;
    return or2(getFileInfoField(dropExtension(f), "Title"), "Unnamed");
  }

  static public boolean isImageServerSnippet(long id) {
    return id >= 1100000 && id < 1200000;
  }

  static public String imageServerURL() {
    return or2(trim(loadTextFile(javaxDataDir("image-server-url.txt"))), "http://botcompany.de/images/raw/");
  }

  static public String muricaCredentialsQuery() {
    return htmlQuery(muricaCredentials());
  }

  static public boolean isGeneralFileServerSnippet(long id) {
    return id >= 1400000 && id < 1500000;
  }

  static public String tb_mainServer_default = "https://code.botcompany.de:9898";

  static public Object tb_mainServer_override;

  static public String tb_mainServer() {
    if (tb_mainServer_override != null)
      return (String) callF(tb_mainServer_override);
    return trim(loadTextFile(tb_mainServer_file(), tb_mainServer_default));
  }

  static public File tb_mainServer_file() {
    return getProgramFile("#1001638", "mainserver.txt");
  }

  static public boolean tb_mainServer_isDefault() {
    return eq(tb_mainServer(), tb_mainServer_default);
  }

  static public String standardCredentials_noCookies() {
    return standardCredentials() + "&noCookies=1";
  }

  static public int loadPage_defaultTimeout = 60000;

  static public ThreadLocal<String> loadPage_charset = new ThreadLocal();

  static public boolean loadPage_allowGzip = true, loadPage_debug;

  static public boolean loadPage_anonymous = false;

  static public int loadPage_verboseness = 100000;

  static public int loadPage_retries = 1;

  static public ThreadLocal<Boolean> loadPage_silent = new ThreadLocal();

  static volatile public int loadPage_forcedTimeout;

  static public ThreadLocal<Integer> loadPage_forcedTimeout_byThread = new ThreadLocal();

  static public ThreadLocal<Map<String, List<String>>> loadPage_responseHeaders = new ThreadLocal();

  static public ThreadLocal<Map<String, String>> loadPage_extraHeaders = new ThreadLocal();

  static public ThreadLocal<Long> loadPage_sizeLimit = new ThreadLocal();

  public static String loadPageSilently(String url) {
    try {
      return loadPageSilently(new URL(loadPage_preprocess(url)));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static String loadPageSilently(URL url) {
    try {
      if (!networkAllowanceTest(str(url)))
        throw fail("Not allowed: " + url);
      IOException e = null;
      for (int tries = 0; tries < loadPage_retries; tries++) try {
        URLConnection con = loadPage_openConnection(url);
        return loadPage(con, url);
      } catch (IOException _e) {
        e = _e;
        if (loadPage_debug)
          print(exceptionToStringShort(e));
        if (tries < loadPage_retries - 1)
          sleepSeconds(1);
      }
      throw e;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public String loadPage_preprocess(String url) {
    if (url.startsWith("tb/"))
      url = tb_mainServer() + "/" + url;
    if (url.indexOf("://") < 0)
      url = "http://" + url;
    return url;
  }

  static public String loadPage(String url) {
    try {
      url = loadPage_preprocess(url);
      if (!isTrue(loadPage_silent.get()))
        printWithTime("Loading: " + hideCredentials(url));
      return loadPageSilently(new URL(url));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public String loadPage(URL url) {
    return loadPage(url.toExternalForm());
  }

  static public String loadPage(URLConnection con, URL url) throws IOException {
    return loadPage(con, url, true);
  }

  static public String loadPage(URLConnection con, URL url, boolean addHeaders) {
    try {
      Map<String, String> extraHeaders = getAndClearThreadLocal(loadPage_extraHeaders);
      if (addHeaders)
        try {
          if (!loadPage_anonymous)
            setHeaders(con);
          if (loadPage_allowGzip)
            con.setRequestProperty("Accept-Encoding", "gzip");
          con.setRequestProperty("X-No-Cookies", "1");
          for (String key : keys(extraHeaders)) con.setRequestProperty(key, extraHeaders.get(key));
        } catch (Throwable e) {
        }
      return loadPage(con);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public String loadPage(URLConnection con) {
    try {
      Long limit = optPar(loadPage_sizeLimit);
      URL url = con.getURL();
      vm_generalSubMap("URLConnection per thread").put(currentThread(), con);
      loadPage_responseHeaders.set(con.getHeaderFields());
      InputStream in = null;
      try {
        in = urlConnection_getInputStream(con);
        if (loadPage_debug)
          print("Put stream in map: " + currentThread());
        String contentType = con.getContentType();
        if (contentType == null) {
          throw new IOException("Page could not be read: " + hideCredentials(url));
        }
        String charset = loadPage_charset == null ? null : loadPage_charset.get();
        if (charset == null)
          charset = loadPage_guessCharset(contentType);
        if ("gzip".equals(con.getContentEncoding())) {
          if (loadPage_debug)
            print("loadPage: Using gzip.");
          in = newGZIPInputStream(in);
        }
        Reader r;
        try {
          r = new InputStreamReader(in, unquote(charset));
        } catch (UnsupportedEncodingException e) {
          print(toHex(utf8(charset)));
          throw e;
        }
        boolean silent = isTrue(loadPage_silent.get());
        StringBuilder buf = new StringBuilder();
        int n = 0;
        while (limit == null || n < limit) {
          ping();
          int ch = r.read();
          if (ch < 0)
            break;
          buf.append((char) ch);
          ++n;
          if (!silent && (n % loadPage_verboseness) == 0)
            print("  " + n + " chars read");
        }
        return buf.toString();
      } finally {
        if (loadPage_debug)
          print("loadPage done");
        vm_generalSubMap("URLConnection per thread").remove(currentThread());
        if (in != null)
          in.close();
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public String loadPage_guessCharset(String contentType) {
    Matcher m = regexpMatcher("text/[a-z]+;\\s*charset=([^\\s]+)\\s*", contentType);
    String match = m.matches() ? m.group(1) : null;
    if (loadPage_debug)
      print("loadPage: contentType=" + contentType + ", match: " + match);
    return or(match, "UTF-8");
  }

  static public URLConnection loadPage_openConnection(URL url) {
    URLConnection con = openConnection(url);
    int timeout = toInt(loadPage_forcedTimeout_byThread.get());
    if (timeout == 0)
      timeout = loadPage_forcedTimeout;
    if (timeout != 0)
      setURLConnectionTimeouts(con, loadPage_forcedTimeout);
    else
      setURLConnectionDefaultTimeouts(con, loadPage_defaultTimeout);
    return con;
  }

  static public boolean saveTextFileIfChanged(File f, String contents) {
    return saveTextFileIfDifferent(f, contents);
  }

  static public File snippetTitle_cacheFile(String snippetID) {
    return javaxCachesDir("Snippet Titles/" + psI(snippetID));
  }

  static public <A> A proxy(Class<A> intrface, final Object target) {
    if (target == null)
      return null;
    if (isInstance(intrface, target))
      return (A) target;
    return (A) java.lang.reflect.Proxy.newProxyInstance(intrface.getClassLoader(), new Class[] { intrface }, new proxy_InvocationHandler(target));
  }

  static public <A> A proxy(Object target, Class<A> intrface) {
    return proxy(intrface, target);
  }

  static public long psI(String snippetID) {
    return parseSnippetID(snippetID);
  }

  static public File localSnippetFile(long snippetID) {
    return localSnippetsDir(snippetID + ".text");
  }

  static public File localSnippetFile(String snippetID) {
    return localSnippetFile(parseSnippetID(snippetID));
  }

  static public String getFileInfoField(File f, String field) {
    return getOneLineFileInfoField(f, field);
  }

  static public File dropExtension(File f) {
    return f == null ? null : fileInSameDir(f, dropExtension(f.getName()));
  }

  static public String dropExtension(String s) {
    return takeFirst(s, smartLastIndexOf(s, '.'));
  }

  static public String htmlQuery(Map params) {
    return empty(params) ? "" : "?" + makePostData(params);
  }

  static public String htmlQuery(Object... data) {
    return empty(data) ? "" : "?" + makePostData(data);
  }

  static public Object[] muricaCredentials() {
    String pass = muricaPassword();
    return nempty(pass) ? new Object[] { "_pass", pass } : new Object[0];
  }

  static public String standardCredentials() {
    String user = standardCredentialsUser();
    String pass = standardCredentialsPass();
    if (nempty(user) && nempty(pass))
      return "&_user=" + urlencode(user) + "&_pass=" + urlencode(pass);
    return "";
  }

  static public boolean networkAllowanceTest(String url) {
    return isAllowed("networkAllowanceTest", url);
  }

  static public <A> A printWithTime(A a) {
    return printWithTime("", a);
  }

  static public <A> A printWithTime(String s, A a) {
    print(hmsWithColons() + ": " + s, a);
    return a;
  }

  static public void setHeaders(URLConnection con) throws IOException {
    String computerID = getComputerID_quick();
    if (computerID != null)
      try {
        con.setRequestProperty("X-ComputerID", computerID);
        con.setRequestProperty("X-OS", System.getProperty("os.name") + " " + System.getProperty("os.version"));
      } catch (Throwable e) {
      }
  }

  static public <A> A optPar(ThreadLocal<A> tl, A defaultValue) {
    A a = tl.get();
    if (a != null) {
      tl.set(null);
      return a;
    }
    return defaultValue;
  }

  static public <A> A optPar(ThreadLocal<A> tl) {
    return optPar(tl, null);
  }

  static public Object optPar(Object[] params, String name) {
    return optParam(params, name);
  }

  static public Object optPar(String name, Object[] params) {
    return optParam(params, name);
  }

  static public Object optPar(String name, Map params) {
    return optParam(name, params);
  }

  static public <A> A optPar(Object[] params, String name, A defaultValue) {
    return optParam(params, name, defaultValue);
  }

  static public <A> A optPar(String name, Object[] params, A defaultValue) {
    return optParam(params, name, defaultValue);
  }

  static public InputStream urlConnection_getInputStream(URLConnection con) throws IOException {
    return con.getInputStream();
  }

  static public String toHex(byte[] bytes) {
    return bytesToHex(bytes);
  }

  static public String toHex(byte[] bytes, int ofs, int len) {
    return bytesToHex(bytes, ofs, len);
  }

  static public byte[] utf8(String s) {
    return toUtf8(s);
  }

  static public Matcher regexpMatcher(String pat, String s) {
    return compileRegexp(pat).matcher(unnull(s));
  }

  static public Matcher regexpMatcher(java.util.regex.Pattern pat, String s) {
    return pat.matcher(unnull(s));
  }

  static public URLConnection openConnection(String url) {
    try {
      return openConnection(new URL(url));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public URLConnection openConnection(URL url) {
    try {
      ping();
      callOpt(javax(), "recordOpenURLConnection", str(url));
      return url.openConnection();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public URLConnection setURLConnectionTimeouts(URLConnection con, long timeout) {
    con.setConnectTimeout(toInt(timeout));
    con.setReadTimeout(toInt(timeout));
    if (con.getConnectTimeout() != timeout || con.getReadTimeout() != timeout)
      print("Warning: Timeouts not set by JDK.");
    return con;
  }

  static public URLConnection setURLConnectionDefaultTimeouts(URLConnection con, long timeout) {
    if (con.getConnectTimeout() == 0) {
      con.setConnectTimeout(toInt(timeout));
      if (con.getConnectTimeout() != timeout)
        print("Warning: URL connect timeout not set by JDK.");
    }
    if (con.getReadTimeout() == 0) {
      con.setReadTimeout(toInt(timeout));
      if (con.getReadTimeout() != timeout)
        print("Warning: URL read timeout not set by JDK.");
    }
    return con;
  }

  static public boolean saveTextFileIfDifferent(File f, String contents) {
    if (eq(loadTextFile(f), contents))
      return false;
    {
      saveTextFile(f, contents);
      return true;
    }
  }

  static public File javaxCachesDir_dir;

  static public File javaxCachesDir() {
    return javaxCachesDir_dir != null ? javaxCachesDir_dir : new File(userHome(), "JavaX-Caches");
  }

  static public File javaxCachesDir(String sub) {
    return newFile(javaxCachesDir(), sub);
  }

  static public File localSnippetsDir() {
    return javaxDataDir("Personal Programs");
  }

  static public File localSnippetsDir(String sub) {
    return newFile(localSnippetsDir(), sub);
  }

  static public String getOneLineFileInfoField(File f, String field) {
    File infoFile = associatedInfosFile(f);
    List<String> lines = lines(loadTextFile(infoFile));
    return firstStartingWithIC_drop(lines, field + ": ");
  }

  static public File fileInSameDir(File f, String newName) {
    return newFile(parentFile(f), newName);
  }

  static public int smartLastIndexOf(String s, char c) {
    if (s == null)
      return 0;
    int i = s.lastIndexOf(c);
    return i >= 0 ? i : l(s);
  }

  static public <A> int smartLastIndexOf(List<A> l, A sub) {
    int i = lastIndexOf(l, sub);
    return i < 0 ? l(l) : i;
  }

  static public String makePostData(Map map) {
    StringBuilder buf = new StringBuilder();
    for (Map.Entry<Object, Object> e : castMapToMapO(map).entrySet()) {
      String key = (String) (e.getKey());
      Object val = e.getValue();
      if (val != null) {
        String value = str(val);
        if (nempty(buf))
          buf.append("&");
        buf.append(urlencode(key)).append("=").append(urlencode((value)));
      }
    }
    return str(buf);
  }

  static public String makePostData(Object... params) {
    StringBuilder buf = new StringBuilder();
    int n = l(params);
    for (int i = 0; i + 1 < n; i += 2) {
      String key = (String) (params[i]);
      Object val = params[i + 1];
      if (val != null) {
        String value = str(val);
        if (nempty(buf))
          buf.append("&");
        buf.append(urlencode(key)).append("=").append(urlencode((value)));
      }
    }
    return str(buf);
  }

  static volatile public boolean muricaPassword_pretendNotAuthed = false;

  static public String muricaPassword() {
    if (muricaPassword_pretendNotAuthed)
      return null;
    return trim(loadTextFile(muricaPasswordFile()));
  }

  static public String standardCredentialsUser() {
    return trim(loadTextFile(oneOfTheFiles(javaxSecretDir("tinybrain-username"), userDir(".tinybrain/username"))));
  }

  static public String standardCredentialsPass() {
    return trim(loadTextFile(oneOfTheFiles(javaxSecretDir("tinybrain-userpass"), userDir(".tinybrain/userpass"))));
  }

  static public String urlencode(String x) {
    try {
      return URLEncoder.encode(unnull(x), "UTF-8");
    } catch (UnsupportedEncodingException e) {
      throw new RuntimeException(e);
    }
  }

  static volatile public Object isAllowed_function;

  static volatile public boolean isAllowed_all = true;

  static public boolean isAllowed(String askingMethod, Object... args) {
    Object f = vm_generalMap_get("isAllowed_function");
    if (f != null && !isTrue(callF(f, askingMethod, args)))
      return false;
    return isAllowed_all || isTrue(callF(isAllowed_function, askingMethod, args));
  }

  static public String hmsWithColons() {
    return hmsWithColons(now());
  }

  static public String hmsWithColons(long time) {
    return new SimpleDateFormat("HH:mm:ss").format(time);
  }

  static public String getComputerID_quick() {
    return computerID();
  }

  static public <A> A optParam(ThreadLocal<A> tl, A defaultValue) {
    return optPar(tl, defaultValue);
  }

  static public <A> A optParam(ThreadLocal<A> tl) {
    return optPar(tl);
  }

  static public Object optParam(String name, Map params) {
    return mapGet(params, name);
  }

  static public <A> A optParam(Object[] opt, String name, A defaultValue) {
    int n = l(opt);
    if (n == 1 && opt[0] instanceof Map) {
      Map map = (Map) (opt[0]);
      return map.containsKey(name) ? (A) map.get(name) : defaultValue;
    }
    if (!even(l(opt)))
      throw fail("Odd parameter length");
    for (int i = 0; i < l(opt); i += 2) if (eq(opt[i], name))
      return (A) opt[i + 1];
    return defaultValue;
  }

  static public Object optParam(Object[] opt, String name) {
    return optParam(opt, name, null);
  }

  static public Object optParam(String name, Object[] params) {
    return optParam(params, name);
  }

  static public File associatedInfosFile(File f) {
    return replaceExtension(f, ".infos");
  }

  static public String firstStartingWithIC_drop(Collection<String> l, final String prefix) {
    for (String s : unnull(l)) if (swic(s, prefix))
      return substring(s, l(prefix));
    return null;
  }

  static public String firstStartingWithIC_drop(String prefix, Collection<String> l) {
    return firstStartingWithIC_drop(l, prefix);
  }

  static public File parentFile(File f) {
    return dirOfFile(f);
  }

  static public Map<Object, Object> castMapToMapO(Map map) {
    return map;
  }

  static public File muricaPasswordFile() {
    return new File(javaxSecretDir(), "murica/muricaPasswordFile");
  }

  static public File oneOfTheFiles(String... paths) {
    if (paths != null)
      for (String path : paths) if (fileExists(path))
        return newFile(path);
    return null;
  }

  static public File oneOfTheFiles(File... files) {
    return oneOfTheFiles(asList(files));
  }

  static public File oneOfTheFiles(Iterable<File> files) {
    if (files != null)
      for (File f : files) if (fileExists(f))
        return f;
    return null;
  }

  static public File javaxSecretDir_dir;

  static public File javaxSecretDir() {
    return javaxSecretDir_dir != null ? javaxSecretDir_dir : new File(userHome(), "JavaX-Secret");
  }

  static public File javaxSecretDir(String sub) {
    return newFile(javaxSecretDir(), sub);
  }

  static public String _computerID;

  static public Lock computerID_lock = lock();

  public static String computerID() {
    if (_computerID == null) {
      Lock __0 = computerID_lock;
      lock(__0);
      try {
        if (_computerID != null)
          return _computerID;
        File file = computerIDFile();
        _computerID = loadTextFile(file.getPath());
        if (_computerID == null) {
          _computerID = loadTextFile(userDir(".tinybrain/computer-id"));
          if (_computerID == null)
            _computerID = makeRandomID(12, new SecureRandom());
          saveTextFile(file, _computerID);
        }
      } finally {
        unlock(__0);
      }
    }
    return _computerID;
  }

  static public boolean even(int i) {
    return (i & 1) == 0;
  }

  static public boolean even(long i) {
    return (i & 1) == 0;
  }

  static public boolean even(BigInteger n) {
    return even(n.intValue());
  }

  static public File replaceExtension(File f, String extOld, String extNew) {
    return newFile(replaceExtension(f2s(f), extOld, extNew));
  }

  static public File replaceExtension(File f, String extNew) {
    return replaceExtension(f, fileExtension(f), extNew);
  }

  static public String replaceExtension(String s, String extOld, String extNew) {
    s = dropSuffixIC(addPrefixOptIfNempty(".", extOld), s);
    return s + addPrefixOptIfNempty(".", extNew);
  }

  static public String replaceExtension(String name, String extNew) {
    return replaceExtension(name, fileExtension(name), extNew);
  }

  static public File computerIDFile() {
    return javaxDataDir("Basic Info/computer-id.txt");
  }

  static public String fileExtension(File f) {
    if (f == null)
      return null;
    return fileExtension(f.getName());
  }

  static public String fileExtension(String s) {
    return substring(s, smartLastIndexOf(s, '.'));
  }

  static public String dropSuffixIC(String suffix, String s) {
    return s == null ? null : ewic(s, suffix) ? s.substring(0, l(s) - l(suffix)) : s;
  }

  static public String addPrefixOptIfNempty(String prefix, String s) {
    return addPrefixIfNotEmpty2(prefix, s);
  }

  static public String addPrefixIfNotEmpty2(String prefix, String s) {
    return empty(s) ? "" : addPrefix(prefix, s);
  }

  static public String addPrefix(String prefix, String s) {
    return s.startsWith(prefix) ? s : prefix + s;
  }

  static public class CountingOutputStream extends FilterOutputStream {

    public long counter;

    public CountingOutputStream(OutputStream out) {
      super(out);
    }

    @Override
    public void write(int b) throws IOException {
      ++counter;
      out.write(b);
    }

    @Override
    public void write(byte[] b) throws IOException {
      counter += b.length;
      out.write(b, 0, b.length);
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
      if (len == 0)
        return;
      counter += len;
      out.write(b, off, len);
    }

    public long getFilePointer() {
      return counter;
    }
  }

  static public class ProgramScan {

    static public int threads = isWindows() ? 500 : 10;

    static public int timeout = 5000;

    static public String ip = "127.0.0.1";

    static public int quickScanFrom = 10000, quickScanTo = 10999;

    static public int maxNumberOfVMs_android = 4;

    static public int maxNumberOfVMs_nonAndroid = 50;

    static public int maxNumberOfVMs;

    static public boolean verbose = false;

    static public class Program {

      public int port;

      public String helloString;

      public Program(int port, String helloString) {
        this.helloString = helloString;
        this.port = port;
      }
    }

    static public List<Program> scan() {
      try {
        return scan(1, 65535);
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    static public List<Program> scan(int fromPort, int toPort) {
      return scan(fromPort, toPort, new int[0]);
    }

    static public List<Program> scan(int fromPort, int toPort, int[] preferredPorts) {
      try {
        Set<Integer> preferredPortsSet = new HashSet<Integer>(asList(preferredPorts));
        int scanSize = toPort - fromPort + 1;
        String name = toPort < 10000 ? "bot" : "program";
        int threads = isWindows() ? min(500, scanSize) : min(scanSize, 10);
        final ExecutorService es = Executors.newFixedThreadPool(threads);
        if (verbose)
          print(firstToUpper(name) + "-scanning " + ip + " with timeout " + timeout + " ms in " + threads + " threads.");
        startTiming();
        List<Future<Program>> futures = new ArrayList();
        List<Integer> ports = new ArrayList();
        for (int port : preferredPorts) {
          futures.add(checkPort(es, ip, port, timeout));
          ports.add(port);
        }
        for (int port = fromPort; port <= toPort; port++) if (!preferredPortsSet.contains(port) && !forbiddenPort(port)) {
          futures.add(checkPort(es, ip, port, timeout));
          ports.add(port);
        }
        es.shutdown();
        List<Program> programs = new ArrayList();
        long time = now();
        int i = 0;
        for (final Future<Program> f : futures) {
          if (verbose)
            print("Waiting for port " + get(ports, i++) + " at time " + (now() - time));
          Program p = f.get();
          if (p != null)
            programs.add(p);
        }
        if (verbose)
          print("Found " + programs.size() + " " + name + "(s) on " + ip);
        return programs;
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    static public Future<Program> checkPort(final ExecutorService es, final String ip, final int port, final int timeout) {
      return es.submit(new Callable<Program>() {

        @Override
        public Program call() {
          try {
            Socket socket = new Socket();
            try {
              socket.setSoTimeout(timeout);
              socket.connect(new InetSocketAddress(ip, port), timeout);
              BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
              String hello = or(in.readLine(), "?");
              return new Program(port, hello);
            } finally {
              socket.close();
            }
          } catch (Exception ex) {
            return null;
          }
        }
      });
    }

    static public List<Program> quickScan() {
      return scan(quickScanFrom, quickScanTo);
    }

    static public List<Program> quickBotScan() {
      return quickBotScan(new int[0]);
    }

    static public List<Program> quickBotScan(int[] preferredPorts) {
      if (maxNumberOfVMs == 0)
        maxNumberOfVMs = isAndroid() ? maxNumberOfVMs_android : maxNumberOfVMs_nonAndroid;
      return scan(4999, 5000 + maxNumberOfVMs - 1, preferredPorts);
    }
  }

  static public interface IResourceLoader {

    public String loadSnippet(String snippetID);

    public String getTranspiled(String snippetID);

    public int getSnippetType(String snippetID);

    public String getSnippetTitle(String snippetID);

    public File loadLibrary(String snippetID);

    default public File pathToJavaXJar() {
      return pathToJavaxJar_noResourceLoader();
    }

    default public File getSnippetJar(String snippetID, String transpiledSrc) {
      return null;
    }
  }

  static public class Value<A> implements IF0<A>, IFieldsToList {

    public A value;

    public Value() {
    }

    public Value(A value) {
      this.value = value;
    }

    public boolean equals(Object o) {
      if (!(o instanceof Value))
        return false;
      Value __1 = (Value) o;
      return eq(value, __1.value);
    }

    public int hashCode() {
      int h = 82420049;
      h = boostHashCombine(h, _hashCode(value));
      return h;
    }

    public Object[] _fieldsToList() {
      return new Object[] { value };
    }

    public A get() {
      return value;
    }

    public String toString() {
      return str(get());
    }
  }

  static public class proxy_InvocationHandler implements InvocationHandler {

    public Object target;

    public proxy_InvocationHandler() {
    }

    public proxy_InvocationHandler(Object target) {
      this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) {
      return call(target, method.getName(), unnull(args));
    }
  }

  static public class FixedRateTimer extends java.util.Timer implements AutoCloseable {

    public FixedRateTimer() {
      this(false);
    }

    public FixedRateTimer(boolean daemon) {
      this(defaultTimerName(), daemon);
    }

    public FixedRateTimer(String name) {
      this(name, false);
    }

    public FixedRateTimer(String name, boolean daemon) {
      super(name, daemon);
      _registerTimer(this);
    }

    public List<Entry> entries = synchroList();

    static public class Entry implements IFieldsToList {

      public TimerTask task;

      public long firstTime;

      public long period;

      public Entry() {
      }

      public Entry(TimerTask task, long firstTime, long period) {
        this.period = period;
        this.firstTime = firstTime;
        this.task = task;
      }

      public String toString() {
        return shortClassName_dropNumberPrefix(this) + "(" + task + ", " + firstTime + ", " + period + ")";
      }

      public Object[] _fieldsToList() {
        return new Object[] { task, firstTime, period };
      }
    }

    public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
      entries.add(new Entry(task, now() + delay, period));
      super.scheduleAtFixedRate(task, delay, period);
    }

    public void cancel() {
      entries.clear();
      super.cancel();
    }

    public int purge() {
      entries.clear();
      return super.purge();
    }

    public FixedRateTimer changeRate(int newPeriod) {
      Object r = ((SmartTimerTask) first(entries).task).r;
      cancel();
      return doEvery(newPeriod, r);
    }

    public void close() {
      try {
        cancel();
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }
  }

  static public class Flag implements Runnable {

    volatile public boolean up = false;

    public boolean raise() {
      if (up)
        return false;
      synchronized (this) {
        if (up)
          return false;
        up = true;
        notifyAll();
        return true;
      }
    }

    public void run() {
      try {
        raise();
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public void waitUntilUp() {
      try {
        if (up)
          return;
        synchronized (this) {
          while (!up) wait();
        }
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public boolean waitUntilUp(double timeout) {
      if (timeout == infinity()) {
        waitUntilUp();
        return isUp();
      } else
        return waitUntilUp(toMS(timeout));
    }

    public boolean waitUntilUp(long timeout) {
      try {
        if (up)
          return true;
        synchronized (this) {
          if (!up)
            wait(timeout);
        }
        return isUp();
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    final public boolean get() {
      return isUp();
    }

    public boolean isUp() {
      return up;
    }

    public String toString() {
      return isUp() ? "up" : "down";
    }
  }

  static public class T3<A, B, C> {

    public A a;

    public B b;

    public C c;

    public T3() {
    }

    public T3(A a, B b, C c) {
      this.c = c;
      this.b = b;
      this.a = a;
    }

    public T3(T3<A, B, C> t) {
      a = t.a;
      b = t.b;
      c = t.c;
    }

    public int hashCode() {
      return _hashCode(a) + 2 * _hashCode(b) - 4 * _hashCode(c);
    }

    public boolean equals(Object o) {
      if (o == this)
        return true;
      if (!(o instanceof T3))
        return false;
      T3 t = (T3) o;
      return eq(a, t.a) && eq(b, t.b) && eq(c, t.c);
    }

    public String toString() {
      return "(" + quoteBorderless(a) + ", " + quoteBorderless(b) + ", " + quoteBorderless(c) + ")";
    }
  }

  public static boolean isWindows() {
    return System.getProperty("os.name").contains("Windows");
  }

  static public String firstToUpper(String s) {
    if (empty(s))
      return s;
    return Character.toUpperCase(s.charAt(0)) + s.substring(1);
  }

  static public long stopTiming_defaultMin = 10;

  static public long startTiming_startTime;

  static public void startTiming() {
    startTiming_startTime = now();
  }

  static public void stopTiming() {
    stopTiming(null);
  }

  static public void stopTiming(String text) {
    stopTiming(text, stopTiming_defaultMin);
  }

  static public void stopTiming(String text, long minToPrint) {
    long time = now() - startTiming_startTime;
    if (time >= minToPrint) {
      text = or2(text, "Time: ");
      print(text + time + " ms");
    }
  }

  static public File loadLibrary(String snippetID) {
    return loadBinarySnippet(snippetID);
  }

  static public File pathToJavaxJar() {
    IResourceLoader rl = vm_getResourceLoader();
    if (rl != null)
      return rl.pathToJavaXJar();
    return pathToJavaxJar_noResourceLoader();
  }

  static public File pathToJavaxJar_noResourceLoader() {
    try {
      int x = latestInstalledJavaX();
      File xfile = new File(userHome(), ".javax/x" + Math.max(x, 30) + ".jar");
      if (!xfile.isFile()) {
        print("Saving " + f2s(xfile));
        String url = x30JarServerURL();
        byte[] data = loadBinaryPage(url);
        if (data.length < 1000000)
          throw fail("Could not load " + url);
        saveBinaryFile(xfile.getPath(), data);
      }
      return xfile;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public Set<java.util.Timer> _registerTimer_list = newWeakHashSet();

  static public void _registerTimer(java.util.Timer timer) {
    _registerTimer_list.add(timer);
  }

  static public void cleanMeUp__registerTimer() {
    cancelTimers(getAndClearList(_registerTimer_list));
  }

  static public double infinity() {
    return positiveInfinity();
  }

  static public String quoteBorderless(Object o) {
    if (o == null)
      return "null";
    return quoteBorderless(str(o));
  }

  static public String quoteBorderless(String s) {
    if (s == null)
      return "null";
    StringBuilder out = new StringBuilder((int) (l(s) * 1.5));
    quoteBorderless_impl(s, out);
    return out.toString();
  }

  static public void quoteBorderless_impl(String s, StringBuilder out) {
    int l = s.length();
    for (int i = 0; i < l; i++) {
      char c = s.charAt(i);
      if (c == '\\' || c == '"')
        out.append('\\').append(c);
      else if (c == '\r')
        out.append("\\r");
      else if (c == '\n')
        out.append("\\n");
      else
        out.append(c);
    }
  }

  static public File loadBinarySnippet(String snippetID) {
    IResourceLoader rl = vm_getResourceLoader();
    if (rl != null)
      return rl.loadLibrary(snippetID);
    return loadBinarySnippet_noResourceLoader(snippetID);
  }

  static public File loadBinarySnippet_noResourceLoader(String snippetID) {
    try {
      long id = parseSnippetID(snippetID);
      if (isImageServerSnippet(id))
        return loadImageAsFile(snippetID);
      File f = DiskSnippetCache_getLibrary(id);
      if (fileSize(f) == 0)
        f = loadDataSnippetToFile_noResourceLoader(snippetID);
      return f;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public int latestInstalledJavaX() {
    File[] files = new File(userHome(), ".javax").listFiles();
    int v = 0;
    if (files != null)
      for (File f : files) {
        Matcher m = regexpMatcher("x(\\d\\d\\d?)\\.jar", f.getName());
        if (m.matches())
          v = Math.max(v, Integer.parseInt(m.group(1)));
      }
    return v;
  }

  static public String x30JarServerURL() {
    return "https://botcompany.de:9898/x30.jar";
  }

  static public ThreadLocal<Map<String, List<String>>> loadBinaryPage_responseHeaders = new ThreadLocal();

  static public ThreadLocal<Map<String, String>> loadBinaryPage_extraHeaders = new ThreadLocal();

  static public byte[] loadBinaryPage(String url) {
    try {
      print("Loading " + url);
      return loadBinaryPage(loadPage_openConnection(new URL(url)));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public byte[] loadBinaryPage(URLConnection con) {
    try {
      Map<String, String> extraHeaders = getAndClearThreadLocal(loadBinaryPage_extraHeaders);
      setHeaders(con);
      for (String key : keys(extraHeaders)) con.setRequestProperty(key, extraHeaders.get(key));
      return loadBinaryPage_noHeaders(con);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public byte[] loadBinaryPage_noHeaders(URLConnection con) {
    try {
      ByteArrayOutputStream buf = new ByteArrayOutputStream();
      InputStream inputStream = con.getInputStream();
      loadBinaryPage_responseHeaders.set(con.getHeaderFields());
      long len = 0;
      try {
        len = con.getContentLength();
      } catch (Throwable e) {
        printStackTrace(e);
      }
      int n = 0;
      while (true) {
        int ch = inputStream.read();
        if (ch < 0)
          break;
        buf.write(ch);
        if (++n % 100000 == 0)
          println("  " + n + (len != 0 ? "/" + len : "") + " bytes loaded.");
      }
      inputStream.close();
      return buf.toByteArray();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static byte[] saveBinaryFile(String fileName, byte[] contents) {
    try {
      File file = new File(fileName);
      File parentFile = file.getParentFile();
      if (parentFile != null)
        parentFile.mkdirs();
      String tempFileName = fileName + "_temp";
      try (FileOutputStream fileOutputStream = newFileOutputStream(tempFileName)) {
        fileOutputStream.write(contents);
      }
      if (file.exists() && !file.delete())
        throw new IOException("Can't delete " + fileName);
      if (!new File(tempFileName).renameTo(file))
        throw new IOException("Can't rename " + tempFileName + " to " + fileName);
      vmBus_send("wroteFile", file);
      return contents;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public byte[] saveBinaryFile(File fileName, byte[] contents) {
    return saveBinaryFile(fileName.getPath(), contents);
  }

  static public <A> Set<A> newWeakHashSet() {
    return synchroWeakHashSet();
  }

  static public void cancelTimers(Collection timers) {
    for (Object timer : timers) cancelTimer(timer);
  }

  static public <A> List<A> getAndClearList(Collection<A> l) {
    if (l == null)
      return emptyList();
    synchronized (collectionMutex(l)) {
      List<A> out = cloneList(l);
      l.clear();
      return out;
    }
  }

  static public double positiveInfinity() {
    return Double.POSITIVE_INFINITY;
  }

  static public File loadImageAsFile(String snippetIDOrURL) {
    try {
      if (isURL(snippetIDOrURL))
        throw fail("not implemented");
      if (!isSnippetID(snippetIDOrURL))
        throw fail("Not a URL or snippet ID: " + snippetIDOrURL);
      String snippetID = "" + parseSnippetID(snippetIDOrURL);
      File file = imageSnippetCacheFile(snippetID);
      if (fileSize(file) > 0)
        return file;
      String imageURL = snippetImageURL_noHttps(snippetID);
      System.err.println("Loading image: " + imageURL);
      byte[] data = loadBinaryPage(imageURL);
      saveBinaryFile(file, data);
      return file;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public File DiskSnippetCache_file(long snippetID) {
    return new File(getGlobalCache(), "data_" + snippetID + ".jar");
  }

  public static File DiskSnippetCache_getLibrary(long snippetID) throws IOException {
    File file = DiskSnippetCache_file(snippetID);
    return file.exists() ? file : null;
  }

  public static File DiskSnippetCache_getLibrary(String snippetID) {
    try {
      return DiskSnippetCache_getLibrary(psI(snippetID));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static void DiskSnippetCache_putLibrary(long snippetID, byte[] data) throws IOException {
    saveBinaryFile(DiskSnippetCache_file(snippetID), data);
  }

  static public byte[] loadDataSnippetImpl(String snippetID) throws IOException {
    byte[] data;
    try {
      URL url = new URL(dataSnippetLink(snippetID));
      print("Loading library: " + hideCredentials(url));
      try {
        data = loadBinaryPage(url.openConnection());
      } catch (RuntimeException e) {
        data = null;
      }
      if (data == null || data.length == 0) {
        url = new URL(tb_mainServer() + "/blobs/" + parseSnippetID(snippetID));
        print("Loading library: " + hideCredentials(url));
        data = loadBinaryPage(url.openConnection());
      }
      print("Bytes loaded: " + data.length);
    } catch (FileNotFoundException e) {
      throw new IOException("Binary snippet #" + snippetID + " not found or not public");
    }
    return data;
  }

  static public long fileSize(String path) {
    return getFileSize(path);
  }

  static public long fileSize(File f) {
    return getFileSize(f);
  }

  static public File loadDataSnippetToFile(String snippetID) {
    try {
      IResourceLoader rl = vm_getResourceLoader();
      if (rl != null)
        return rl.loadLibrary(snippetID);
      return loadDataSnippetToFile_noResourceLoader(snippetID);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public File loadDataSnippetToFile_noResourceLoader(String snippetID) {
    try {
      snippetID = fsI(snippetID);
      File f = DiskSnippetCache_file(parseSnippetID(snippetID));
      List<URL> urlsTried = new ArrayList();
      List<Throwable> errors = new ArrayList();
      try {
        URL url = addAndReturn(urlsTried, new URL(dataSnippetLink(snippetID)));
        print("Loading library: " + hideCredentials(url));
        try {
          loadBinaryPageToFile(openConnection(url), f);
          if (fileSize(f) == 0)
            throw fail();
        } catch (Throwable e) {
          errors.add(e);
          url = addAndReturn(urlsTried, new URL(tb_mainServer() + "/blobs/" + psI(snippetID)));
          print(e);
          print("Trying other server: " + hideCredentials(url));
          loadBinaryPageToFile(openConnection(url), f);
          print("Got bytes: " + fileSize(f));
        }
        if (fileSize(f) == 0)
          throw fail();
        System.err.println("Bytes loaded: " + fileSize(f));
      } catch (Throwable e) {
        errors.add(e);
        throw fail("Binary snippet " + snippetID + " not found or not public. URLs tried: " + allToString(urlsTried) + ", errors: " + allToString(errors));
      }
      return f;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public <A> A println(A a) {
    return print(a);
  }

  static public <A> Set<A> synchroWeakHashSet() {
    return Collections.newSetFromMap((Map) newWeakHashMap());
  }

  static public boolean loadBufferedImage_useImageCache = true;

  static public BufferedImage loadBufferedImage(String snippetIDOrURLOrFile) {
    try {
      ping();
      if (snippetIDOrURLOrFile == null)
        return null;
      if (isURL(snippetIDOrURLOrFile))
        return imageIO_readURL(snippetIDOrURLOrFile);
      if (isSnippetID(snippetIDOrURLOrFile)) {
        String snippetID = "" + parseSnippetID(snippetIDOrURLOrFile);
        IResourceLoader rl = vm_getResourceLoader();
        if (rl != null)
          return loadBufferedImage(rl.loadLibrary(snippetID));
        File dir = imageSnippetsCacheDir();
        if (loadBufferedImage_useImageCache) {
          dir.mkdirs();
          File file = new File(dir, snippetID + ".png");
          if (file.exists() && file.length() != 0)
            try {
              return ImageIO.read(file);
            } catch (Throwable e) {
              e.printStackTrace();
            }
        }
        String imageURL = snippetImageURL_http(snippetID);
        print("Loading image: " + imageURL);
        BufferedImage image = imageIO_readURL(imageURL);
        if (loadBufferedImage_useImageCache) {
          File tempFile = new File(dir, snippetID + ".tmp." + System.currentTimeMillis());
          ImageIO.write(image, "png", tempFile);
          tempFile.renameTo(new File(dir, snippetID + ".png"));
        }
        return image;
      } else
        return loadBufferedImage(new File(snippetIDOrURLOrFile));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public BufferedImage loadBufferedImage(File file) {
    return loadBufferedImageFile(file);
  }

  static public boolean isURL(String s) {
    return startsWithOneOf(s, "http://", "https://", "file:");
  }

  static public File imageSnippetCacheFile(String snippetID) {
    File dir = imageSnippetsCacheDir();
    if (!loadBufferedImage_useImageCache)
      return null;
    return new File(dir, parseSnippetID(snippetID) + ".png");
  }

  static public String snippetImageURL_noHttps(String snippetID) {
    return snippetImageURL_noHttps(snippetID, "png");
  }

  static public String snippetImageURL_noHttps(String snippetID, String contentType) {
    return snippetImageURL(snippetID, contentType).replace("https://www.botcompany.de:8443/", "http://www.botcompany.de:8080/").replace("https://botcompany.de/", "http://botcompany.de/");
  }

  static public File getGlobalCache() {
    File file = new File(javaxCachesDir(), "Binary Snippets");
    file.mkdirs();
    return file;
  }

  static public String dataSnippetLink(String snippetID) {
    long id = parseSnippetID(snippetID);
    if (id >= 1100000 && id < 1200000)
      return imageServerURL() + id;
    if (id >= 1200000 && id < 1300000) {
      String pw = muricaPassword();
      if (empty(pw))
        throw fail("Please set 'murica password by running #1008829");
      return "https://botcompany.de/files/" + id + "?_pass=" + pw;
    }
    return fileServerURL() + "/" + id;
  }

  static public void loadBinaryPageToFile(String url, File file) {
    try {
      print("Loading " + url);
      loadBinaryPageToFile(openConnection(new URL(url)), file);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public void loadBinaryPageToFile(URLConnection con, File file) {
    try {
      setHeaders(con);
      loadBinaryPageToFile_noHeaders(con, file);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public void loadBinaryPageToFile_noHeaders(URLConnection con, File file) {
    try {
      assertNotNull(file);
      File ftemp = new File(f2s(file) + "_temp");
      FileOutputStream buf = newFileOutputStream(mkdirsFor(ftemp));
      try {
        InputStream inputStream = con.getInputStream();
        long len = 0;
        try {
          len = con.getContentLength();
        } catch (Throwable e) {
          printStackTrace(e);
        }
        String pat = "  {*}" + (len != 0 ? "/" + len : "") + " bytes loaded.";
        copyStreamWithPrints(inputStream, buf, pat);
        inputStream.close();
        buf.close();
        file.delete();
        renameFile_assertTrue(ftemp, file);
      } finally {
        if (buf != null)
          buf.close();
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public BufferedImage imageIO_readURL(String url) {
    try {
      return ImageIO.read(new URL(url));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public File imageSnippetsCacheDir() {
    return javaxCachesDir("Image-Snippets");
  }

  static public String snippetImageURL_http(String snippetID) {
    return snippetImageURL_http(snippetID, "png");
  }

  static public String snippetImageURL_http(String snippetID, String contentType) {
    return replacePrefix("https://", "http://", snippetImageURL(snippetID, contentType)).replace(":8443", ":8080");
  }

  static public BufferedImage loadBufferedImageFile(File file) {
    try {
      if (!isFile(file))
        return null;
      return ImageIO.read(file);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public String snippetImageURL(long snippetID) {
    return snippetImageURL(fsI(snippetID));
  }

  static public String snippetImageURL(String snippetID) {
    return snippetImageURL(snippetID, "png");
  }

  static public String snippetImageURL(String snippetID, String contentType) {
    if (snippetID == null || isURL(snippetID))
      return snippetID;
    long id = parseSnippetID(snippetID);
    String url;
    if (isImageServerSnippet(id))
      url = imageServerLink(id);
    else
      url = "https://botcompany.de/img/" + id;
    return url;
  }

  static public String fileServerURL() {
    return "https://botcompany.de/files";
  }

  public static File mkdirsFor(File file) {
    return mkdirsForFile(file);
  }

  static public void copyStreamWithPrints(InputStream in, OutputStream out, String pat) {
    try {
      byte[] buf = new byte[65536];
      int total = 0;
      while (true) {
        int n = in.read(buf);
        if (n <= 0)
          return;
        out.write(buf, 0, n);
        if ((total + n) / 100000 > total / 100000)
          print(pat.replace("{*}", str(roundDownTo(100000, total))));
        total += n;
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public File renameFile_assertTrue(File a, File b) {
    try {
      if (a.equals(b))
        return b;
      if (!a.exists())
        throw fail("Source file not found: " + f2s(a));
      if (b.exists())
        throw fail("Target file exists: " + f2s(b));
      mkdirsForFile(b);
      if (!a.renameTo(b))
        throw fail("Can't rename " + f2s(a) + " to " + f2s(b));
      return b;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  static public boolean isFile(File f) {
    return f != null && f.isFile();
  }

  static public boolean isFile(String path) {
    return isFile(newFile(path));
  }

  static public String imageServerLink(String md5OrID) {
    if (possibleMD5(md5OrID))
      return "https://botcompany.de/images/md5/" + md5OrID;
    return imageServerLink(parseSnippetID(md5OrID));
  }

  static public String imageServerLink(long id) {
    return "https://botcompany.de/images/" + id;
  }

  static public boolean possibleMD5(String s) {
    return isMD5(s);
  }

  static public boolean isMD5(String s) {
    return l(s) == 32 && isLowerHexString(s);
  }

  static public boolean isLowerHexString(String s) {
    for (int i = 0; i < l(s); i++) {
      char c = s.charAt(i);
      if (c >= '0' && c <= '9' || c >= 'a' && c <= 'f') {
      } else
        return false;
    }
    return true;
  }
}