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;
// AppendableChain has one "smart" head element (with size counter
// and pointer to the chain's last element), all the other nodes are
// maximally simple (MinimalChain).
// This allows O(1) front insertion, front removal and back insertion
// (not removal at the back though) which is fine for what I need this
// for (event queues).
//
// Stefan Reich, Oct 21
import static x30_pkg.x30_util.DynamicObject;
import java.text.*;
import java.text.NumberFormat;
import java.util.TimeZone;
import java.awt.geom.*;
class main {
static class AppendableChain extends MinimalChain implements Iterable, IntSize {
  MinimalChain last; // pointer to last element in chain (which may be us)
   final public int getSize(){ return size(); }
public int size() { return size; }
 int size; // total length of chain
  AppendableChain() {} // only used internally
  AppendableChain(A element) {
  this.element = element; size = 1; last = this; }
  
  // intermediate constructor called by itemPlusChain()
  AppendableChain(A element, AppendableChain next) {
  this.next = next;
  this.element = element;
    if (next == null) return;
    
    MinimalChain 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()); }
  
  // append at the end
  boolean add(A a) {
    MinimalChain newLast = new MinimalChain(a);
    last.next = newLast;
    last = newLast;
    ++size;
    return true;
  }
  
  // drop first element
  AppendableChain popFirst() {
    if (next == null) return null;
    element = next.element;
    if (last == next) last = this;
    next = next.next;
    --size;
    return this;
  }
  
  ArrayList toList() {
    ArrayList l = emptyList(size);
    MinimalChain c = this;
    while (c != null) {
      l.add(c.element);
      c = c.next;
    }
    return l;
  }
  
  //public Iterator iterator() { ret toList().iterator(); }
  
  class ACIt extends IterableIterator  < A > {
    MinimalChain c = AppendableChain.this;
    
    public boolean hasNext() {
      return c != null;
    }
    
    public A next() {
      var a = c.element;
      c = c.next;
      return a;
    }
  }
  
  public IterableIterator iterator() {
    return new ACIt();
  }
}
static String str(Object o) {
  return o == null ? "null" : o.toString();
}
static String str(char[] c) {
  return new String(c);
}
static String str(char[] c, int offset, int count) {
  return new String(c, offset, count);
}
static  ArrayList toList(A[] a) { return asList(a); }
static ArrayList toList(int[] a) { return asList(a); }
static  ArrayList toList(Set s) { return asList(s); }
static  ArrayList toList(Iterable s) { return asList(s); }
static  A popFirst(List l) {
  if (empty(l)) return null;
  A a = first(l);
  l.remove(0);
  return a;
}
static  A popFirst(Collection l) {
  if (empty(l)) return null;
  A a = first(l);
  l.remove(a);
  return a;
}
static  Pair popFirst(Map 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  List popFirst(int n, List l) {
  List part = cloneSubList(l, 0, n);
  removeSubList(l, 0, n);
  return part;
}
static  AppendableChain popFirst(AppendableChain a) {
  return a == null ? null : a.popFirst();
}
static ArrayList emptyList() {
  return new ArrayList();
  //ret Collections.emptyList();
}
static ArrayList emptyList(int capacity) {
  return new ArrayList(max(0, capacity));
}
// Try to match capacity
static ArrayList emptyList(Iterable l) {
  return l instanceof Collection ? emptyList(((Collection) l).size()) : emptyList();
}
static ArrayList emptyList(Object[] l) {
  return emptyList(l(l));
}
// get correct type at once
static  ArrayList emptyList(Class c) {
  return new ArrayList();
}
static  Iterator iterator(Iterable c) {
  return c == null ? emptyIterator() : c.iterator();
}
// unclear semantics as to whether return null on null
static  ArrayList asList(A[] a) {
  return a == null ? new ArrayList() : new ArrayList(Arrays.asList(a));
}
static ArrayList asList(int[] a) {
  if (a == null) return null;
  ArrayList l = emptyList(a.length);
  for (int i : a) l.add(i);
  return l;
}
static ArrayList asList(long[] a) {
  if (a == null) return null;
  ArrayList l = emptyList(a.length);
  for (long i : a) l.add(i);
  return l;
}
static ArrayList asList(float[] a) {
  if (a == null) return null;
  ArrayList l = emptyList(a.length);
  for (float i : a) l.add(i);
  return l;
}
static ArrayList asList(double[] a) {
  if (a == null) return null;
  ArrayList l = emptyList(a.length);
  for (double i : a) l.add(i);
  return l;
}
static ArrayList asList(short[] a) {
  if (a == null) return null;
  ArrayList l = emptyList(a.length);
  for (short i : a) l.add(i);
  return l;
}
static  ArrayList asList(Iterator it) {
  ArrayList l = new ArrayList();
  if (it != null)
    while (it.hasNext())
      l.add(it.next());
  return l;  
}
// disambiguation
static  ArrayList asList(IterableIterator s) {
  return asList((Iterator) s);
}
static  ArrayList asList(Iterable 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  ArrayList asList(Enumeration e) {
  ArrayList l = new ArrayList();
  if (e != null)
    while (e.hasMoreElements())
      l.add(e.nextElement());
  return l;
}
static  ArrayList asList(ReverseChain c) {
  return c == null ? emptyList() : c.toList();
}
static  List asList(Pair p) {
  return p == null ? null : ll(p.a, p.b);
}
static boolean empty(Collection c) { return c == null || c.isEmpty(); }
static boolean empty(Iterable c) { return c == null || !c.iterator().hasNext(); }
static boolean empty(CharSequence s) { return s == null || s.length() == 0; }
static boolean empty(Map map) { return map == null || map.isEmpty(); }
static boolean empty(Object[] o) { return o == null || o.length == 0; }
static boolean empty(BitSet bs) { return bs == null || bs.isEmpty(); }
static 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 boolean empty(Iterator i) { return i == null || !i.hasNext(); }
static boolean empty(double[] a) { return a == null || a.length == 0; }
static boolean empty(float[] a) { return a == null || a.length == 0; }
static boolean empty(int[] a) { return a == null || a.length == 0; }
static boolean empty(long[] a) { return a == null || a.length == 0; }
static boolean empty(byte[] a) { return a == null || a.length == 0; }
static boolean empty(short[] a) { return a == null || a.length == 0; }
static boolean empty(IMultiMap mm) { return mm == null || mm.size() == 0; }
static boolean empty(File f) { return getFileSize(f) == 0; }
static boolean empty(Rect r) { return !(r != null && r.w != 0 && r.h != 0); }
static boolean empty(Chain c) { return c == null; }
static boolean empty(AppendableChain c) { return c == null; }
static Object first(Object list) {
  return first((Iterable) list);
}
static  A first(List list) {
  return empty(list) ? null : list.get(0);
}
static  A first(A[] bla) {
  return bla == null || bla.length == 0 ? null : bla[0];
}
static  Pair first(Map map) {
  return mapEntryToPair(first(entrySet(map)));
}
static  Pair first(MultiMap 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  A first(IterableIterator i) {
  return first((Iterator) i);
}
static  A first(Iterator i) {
  return i == null || !i.hasNext() ? null : i.next();
}
static  A first(Iterable i) {
  if (i == null) return null;
  Iterator it = i.iterator();
  return it.hasNext() ? it.next() : null;
}
static Character first(String s) { return empty(s) ? null : s.charAt(0); }
static Character first(CharSequence s) { return empty(s) ? null : s.charAt(0); }
static  A first(Pair p) {
  return p == null ? null : p.a;
}
static Byte first(byte[] l) { 
  return empty(l) ? null : l[0];
}
static  A first(A[] l, IF1 pred) {
  return firstThat(l, pred);
}
static  A first(Iterable l, IF1 pred) {
  return firstThat(l, pred);
}
static  A first(IF1 pred, Iterable l) {
  return firstThat(pred, l);
}
static  A first(AppendableChain a) {
  return a == null ? null : a.element;
}
static  Pair mapEntryToPair(Map.Entry e) {
  return e == null ? null : pair(e.getKey(), e.getValue());
}
static  List cloneSubList(List l, int startIndex, int endIndex) {
  return newSubList(l, startIndex, endIndex);
}
static  List cloneSubList(List l, int startIndex) {
  return newSubList(l, startIndex);
}
static void removeSubList(List l, int from, int to) {
  if (l != null) subList(l, from, to).clear();
}
static void removeSubList(List l, int from) {
  if (l != null) subList(l, from).clear();
}
static int max(int a, int b) { return Math.max(a, b); }
static int max(int a, int b, int c) { return max(max(a, b), c); }
static long max(int a, long b) { return Math.max((long) a, b); }
static long max(long a, long b) { return Math.max(a, b); }
static double max(int a, double b) { return Math.max((double) a, b); }
static float max(float a, float b) { return Math.max(a, b); }
static double max(double a, double b) { return Math.max(a, b); }
static > A max (Iterable 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;
}
/*Nah.
static int max(Collection c) {
  int x = Integer.MIN_VALUE;
  for (int i : c) x = max(x, i);
  ret x;
}*/
static 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 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 byte max(byte[] c) {
  byte x = -128;
  for (byte d : c) if (d > x) x = d;
  return x;
}
static short max(short[] c) {
  short x = -0x8000;
  for (short d : c) if (d > x) x = d;
  return x;
}
static int max(int[] c) {
  int x = Integer.MIN_VALUE;
  for (int d : c) if (d > x) x = d;
  return x;
}
static > A max(A a, A b) {
  return cmp(a, b) >= 0 ? a : b;
}
static int l(Object[] a) { return a == null ? 0 : a.length; }
static int l(boolean[] a) { return a == null ? 0 : a.length; }
static int l(byte[] a) { return a == null ? 0 : a.length; }
static int l(short[] a) { return a == null ? 0 : a.length; }
static int l(long[] a) { return a == null ? 0 : a.length; }
static int l(int[] a) { return a == null ? 0 : a.length; }
static int l(float[] a) { return a == null ? 0 : a.length; }
static int l(double[] a) { return a == null ? 0 : a.length; }
static int l(char[] a) { return a == null ? 0 : a.length; }
static int l(Collection c) { return c == null ? 0 : c.size(); }
static int l(Iterator i) { return iteratorCount_int_close(i); } // consumes the iterator && closes it if possible
static int l(Map m) { return m == null ? 0 : m.size(); }
static int l(CharSequence s) { return s == null ? 0 : s.length(); }
static long l(File f) { return f == null ? 0 : f.length(); }
  static int l(IMultiMap mm) { return mm == null ? 0 : mm.size(); }
  static int l(AppendableChain a) { return a == null ? 0 : a.size; }
  static int l(IntSize o) { return o == null ? 0 : o.size(); }
static Iterator emptyIterator() {
  return Collections.emptyIterator();
}
static  List ll(A... a) {
  ArrayList l = new ArrayList(a.length);
  if (a != null) for (A x : a) l.add(x);
  return l;
}
static RuntimeException fail() { throw new RuntimeException("fail"); }
static RuntimeException fail(Throwable e) { throw asRuntimeException(e); }
static RuntimeException fail(Object msg) { throw new RuntimeException(String.valueOf(msg)); }
static RuntimeException fail(Object... objects) { throw new Fail(objects); }
static RuntimeException fail(String msg) { throw new RuntimeException(msg == null ? "" : msg); }
static RuntimeException fail(String msg, Throwable innerException) { throw new RuntimeException(msg, innerException); }
static String getType(Object o) {
  return getClassName(o);
}
static long getFileSize(String path) {
  return path == null ? 0 : new File(path).length();
}
static long getFileSize(File f) {
  return f == null ? 0 : f.length();
}
static  Set> entrySet(Map map) {
  return _entrySet(map);
}
static  Pair pair(A a, B b) {
  return new Pair(a, b);
}
static  Pair pair(A a) {
  return new Pair(a, a);
}
static  A firstThat(Iterable l, IF1 pred) {
  for (A a : unnullForIteration(l))
    if (pred.get(a))
      return a;
  return null;
}
static  A firstThat(A[] l, IF1 pred) {
  for (A a : unnullForIteration(l))
    if (pred.get(a))
      return a;
  return null;
}
static  A firstThat(IF1 pred, Iterable l) {
  return firstThat(l, pred);
}
static  A firstThat(IF1 pred, A[] l) {
  return firstThat(l, pred);
}
static  List newSubList(List l, int startIndex, int endIndex) {
  return cloneList(subList(l, startIndex, endIndex));
}
static  List newSubList(List l, int startIndex) {
  return cloneList(subList(l, startIndex));
}
static  List subList(List l, int startIndex) {
  return subList(l, startIndex, l(l));
}
static  List subList(int startIndex, List l) {
  return subList(l, startIndex);
}
static  List subList(int startIndex, int endIndex, List l) {
  return subList(l, startIndex, endIndex);
}
static  List subList(List 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 int cmp(Number a, Number b) {
  return a == null ? b == null ? 0 : -1 : cmp(a.doubleValue(), b.doubleValue());
}
static int cmp(double a, double b) {
  return a < b ? -1 : a == b ? 0 : 1;
}
static int cmp(int a, int b) {
  return a < b ? -1 : a == b ? 0 : 1;
}
static int cmp(long a, long b) {
  return a < b ? -1 : a == b ? 0 : 1;
}
static 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  int iteratorCount_int_close(Iterator 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 RuntimeException asRuntimeException(Throwable t) {
  
  if (t instanceof Error)
    _handleError((Error) t);
  
  return t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t);
}
static String getClassName(Object o) {
  return o == null ? "null" : o instanceof Class ? ((Class) o).getName() : o.getClass().getName();
}
static  Set> _entrySet(Map map) {
  return map == null ? Collections.EMPTY_SET : map.entrySet();
}
static String unnullForIteration(String s) {
  return s == null ? "" : s;
}
static  Collection unnullForIteration(Collection l) {
  return l == null ? immutableEmptyList() : l;
}
static  List unnullForIteration(List l) { return l == null ? immutableEmptyList() : l; }
static byte[] unnullForIteration(byte[] l) { return l == null ? emptyByteArray() : l; }
static int[] unnullForIteration(int[] l) { return l == null ? emptyIntArray() : l; }
static char[] unnullForIteration(char[] l) { return l == null ? emptyCharArray() : l; }
static double[] unnullForIteration(double[] l) { return l == null ? emptyDoubleArray() : l; }
static short[] unnullForIteration(short[] l) { return l == null ? emptyShortArray() : l; }
static  Map unnullForIteration(Map l) {
  return l == null ? immutableEmptyMap() : l;
}
static  Iterable unnullForIteration(Iterable i) {
  return i == null ? immutableEmptyList() : i;
}
static  A[] unnullForIteration(A[] a) {
  return a == null ? (A[]) emptyObjectArray() : a;
}
static BitSet unnullForIteration(BitSet b) {
  return b == null ? new BitSet() : b;
}
static Pt unnullForIteration(Pt p) {
  return p == null ? new Pt() : p;
}
//ifclass Symbol
static Symbol unnullForIteration(Symbol s) {
  return s == null ? emptySymbol() : s;
}
//endif
static  Pair unnullForIteration(Pair p) {
  return p != null ? p : new Pair(null, null);
}
static long unnullForIteration(Long l) { return l == null ? 0L : l; }
static  ArrayList cloneList(Iterable l) {
  return l instanceof Collection ? cloneList((Collection) l) : asList(l);
}
static  ArrayList cloneList(Collection l) {
  if (l == null) return new ArrayList();
  synchronized(collectionMutex(l)) {
    return new ArrayList(l);
  }
}
static RuntimeException rethrow(Throwable t) {
  
  if (t instanceof Error)
    _handleError((Error) t);
  
  throw t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t);
}
static RuntimeException rethrow(String msg, Throwable t) {
  throw new RuntimeException(msg, t);
}
static void _handleError(Error e) {
  //call(javax(), '_handleError, e);
}
static  List immutableEmptyList() {
  return Collections.emptyList();
}
static byte[] emptyByteArray_a = new byte[0];
static byte[] emptyByteArray() { return emptyByteArray_a; }
static int[] emptyIntArray_a = new int[0];
static int[] emptyIntArray() { return emptyIntArray_a; }
static char[] emptyCharArray = new char[0];
static char[] emptyCharArray() { return emptyCharArray; }
static double[] emptyDoubleArray = new double[0];
static double[] emptyDoubleArray() { return emptyDoubleArray; }
static short[] emptyShortArray = new short[0];
static short[] emptyShortArray() { return emptyShortArray; }
static  Map immutableEmptyMap() {
  return Collections.emptyMap();
}
static Object[] emptyObjectArray_a = new Object[0];
static Object[] emptyObjectArray() { return emptyObjectArray_a; }
static Symbol emptySymbol_value;
static Symbol emptySymbol() {
  if (emptySymbol_value == null) emptySymbol_value = symbol("");
  return emptySymbol_value;
}
// TODO: JDK 17!! ?? No! Yes? Yes!!
static Object collectionMutex(List l) {
  return l;
}
static Object collectionMutex(Object o) {
  
  if (o instanceof List) return o;
  
  // TODO: actually use our own maps so we can get the mutex properly
  
  String c = className(o);
  
    
  
    
  
  return o;
}
static WeakHasherMap symbol_map = new WeakHasherMap(new Hasher() {
  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 Symbol symbol(String s) {
  
  
  if (s == null) return null;
  synchronized(symbol_map) {
    // TODO: avoid object creation by passing the string to findKey
    Symbol symbol = new Symbol(s, true);
    Symbol existingSymbol = symbol_map.findKey(symbol);
    if (existingSymbol == null)
      symbol_map.put(existingSymbol = symbol, true);
    
      
    return existingSymbol;
  }
  
}
static 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 Symbol symbol(Object o) {
  return symbol((CharSequence) o);
}
static String className(Object o) {
  return getClassName(o);
}
static boolean eq(Object a, Object b) {
  return a == b || a != null && b != null && a.equals(b);
}
// a little kludge for stuff like eq(symbol, "$X")
static boolean eq(Symbol a, String b) {
  return eq(str(a), b);
}
// records its full size (total value count) in a field now
static class MultiMap implements IMultiMap {
  Map> data = new HashMap>();
  int fullSize;
  
  MultiMap() {}
  MultiMap(boolean useTreeMap) { if (useTreeMap) data = new TreeMap(); }
  MultiMap(MultiMap map) { putAll(map); }
  MultiMap(Map> data) {
  this.data = data;}
  void put(A key, B value) { synchronized(data) {
    List list = data.get(key);
    if (list == null)
      data.put(key, list = _makeEmptyList());
    list.add(value);
    ++fullSize;
  }}
  void add(A key, B value) { put(key, value); }
  void addAll(A key, Collection values) { putAll(key, values); }
  
  void addAllIfNotThere(A key, Collection values) { synchronized(data) {
    for (B value : values)
      setPut(key, value);
  }}
  
  void setPut(A key, B value) { synchronized(data) {
    if (!containsPair(key, value))
      put(key, value);
  }}
  
  boolean containsPair(A key, B value) { synchronized(data) {
    return get(key).contains(value);
  }}
  
  void putAll(Collection keys, B value) { synchronized(data) {
    for (A key : unnullForIteration(keys))
      put(key, value);
  }}
  void putAll(A key, Collection values) { synchronized(data) {
    if (nempty(values)) getActual(key).addAll(values);
  }}
  void putAll(Iterable> pairs) { synchronized(data) {
    for (Pair p : unnullForIteration(pairs))
      put(p.a, p.b);
  }}
  
  void removeAll(A key, Collection values) { synchronized(data) {
    for (B value : values)
      remove(key, value);
  }}
  
  public List get(A key) { synchronized(data) {
    List list = data.get(key);
    return list == null ? Collections. emptyList() : list;
  }}
  
  List getOpt(A key) { synchronized(data) {
    return data.get(key);
  }}
  List getAndClear(A key) { synchronized(data) {
    List l = cloneList(data.get(key));
    remove(key);
    return l;
  }}
  
  // returns actual mutable live list
  // creates the list if not there
  List getActual(A key) { synchronized(data) {
    List list = data.get(key);
    if (list == null)
      data.put(key, list = _makeEmptyList());
    return list;
  }}
 
  void clean(A key) { synchronized(data) {
    List list = data.get(key);
    if (list != null && list.isEmpty()) {
      fullSize -= l(list);
      data.remove(key);
    }
  }}
  final public Set keys(){ return keySet(); }
public Set keySet() { synchronized(data) {
    return data.keySet();
  }}
  void remove(A key) { synchronized(data) {
    fullSize -= l(this.getOpt(key));
    data.remove(key);
  }}
  
  final void remove(Pair p){ removePair(p); }
void removePair(Pair p) {
    if (p != null) remove(p.a, p.b);
  }
  void remove(A key, B value) { synchronized(data) {
    List list = data.get(key);
    if (list != null) {
      if (list.remove(value))
        fullSize--;
      if (list.isEmpty())
        data.remove(key);
    }
  }}
  void clear() { synchronized(data) {
    data.clear();
  }}
  boolean containsKey(A key) { synchronized(data) {
    return data.containsKey(key);
  }}
  B getFirst(A key) { synchronized(data) {
    List list = get(key);
    return list.isEmpty() ? null : list.get(0);
  }}
  
  void addAll(MultiMap map) { putAll(map); }
  
  void putAll(MultiMap map) { synchronized(data) {
    for (A key : map.keySet())
      putAll(key, map.get(key));
  }}
  
  void putAll(Map map) { synchronized(data) {
    if (map != null) for (Map.Entry 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;
  }}
  
  // expensive operation
  List reverseGet(B b) { synchronized(data) {
    List l = new ArrayList();
    for (A key : data.keySet())
      if (data.get(key).contains(b))
        l.add(key);
    return l;
  }}
  
  Map> asMap() { synchronized(data) {
    return cloneMap(data);
  }}
  
  boolean isEmpty() { synchronized(data) { return data.isEmpty(); }}
  
  // override in subclasses
  List _makeEmptyList() {
    return new ArrayList();
  }
  
  // returns live lists
  Collection> allLists() {
    synchronized(data) {
      return new ArrayList(data.values());
    }
  }
  Collection> values() { return allLists(); }
  
  List allValues() {
    return concatLists(data.values());
  }
  
  Object mutex() { return data; }
  
  public String toString() { return "mm" + str(data); }
}
static interface Hasher {
  int hashCode(A a);
  boolean equals(A a, A b);
}
interface IntSize {
  int size();
}
static class MinimalChain implements Iterable {
  A element;
  MinimalChain next;
  MinimalChain() {}
  MinimalChain(A element) {
  this.element = element;}
  MinimalChain(A element, MinimalChain next) {
  this.next = next;
  this.element = element;}
  
  public String toString() { return str(toList()); }
  
  ArrayList toList() {
    ArrayList l = new ArrayList();
    MinimalChain c = this;
    while (c != null) {
      l.add(c.element);
      c = c.next;
    }
    return l;
  }
  
  void setElement(A a) { element = a; }
  void setNext(MinimalChain next) { this.next = next; }
  
  // TODO: optimize
  public Iterator iterator() { return toList().iterator(); }
  
  A get() { return element; }
}
static interface IF1 {
  B get(A a);
}
// for the version with MasterSymbol (used WAY back in "Smart Bot"!) see #1010608
static class Symbol implements CharSequence {
  String text;
  
  Symbol() {}
  Symbol(String text, boolean dummy) {
  this.text = text;} // weird signature to prevent accidental calling
  
  public int hashCode() { return _hashCode(text); }
  public String toString() { return text; }
  public boolean equals(Object o) {
    return this == o;
  }
  // implementation of CharSequence methods
  
  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);
  }
}
/*
 * @(#)WeakHashMap.java 1.5 98/09/30
 *
 * Copyright 1998 by Sun Microsystems, Inc.,
 * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information
 * of Sun Microsystems, Inc. ("Confidential Information").  You
 * shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement
 * you entered into with Sun.
 */
 
// From https://github.com/mernst/plume-lib/blob/df0bfafc3c16848d88f4ea0ef3c8bf3367ae085e/java/src/plume/WeakHasherMap.java
static final class WeakHasherMap extends AbstractMap implements Map {
    private Hasher hasher = null;
    /*@Pure*/
    private boolean keyEquals(Object k1, Object k2) {
  return (hasher==null ? k1.equals(k2)
           : hasher.equals(k1, k2));
    }
    /*@Pure*/
    private int keyHashCode(Object k1) {
  return (hasher==null ? k1.hashCode()
           : hasher.hashCode(k1));
    }
    // The WeakKey class can't be static because it depends on the hasher.
    // That in turn means that its methods can't be static.
    // However, I need to be able to call the methods such as create() that
    // were static in the original version of this code.
    // This finesses that.
    private /*@Nullable*/ WeakKey WeakKeyCreate(K k) {
  if (k == null) return null;
  else return new WeakKey(k);
    }
    private /*@Nullable*/ WeakKey WeakKeyCreate(K k, ReferenceQueue super K> q) {
  if (k == null) return null;
  else return new WeakKey(k, q);
    }
    // Cannot be a static class: uses keyHashCode() and keyEquals()
    private final class WeakKey extends WeakReference {
  private int hash; /* Hashcode of key, stored here since the key
           may be tossed by the GC */
  private WeakKey(K k) {
      super(k);
      hash = keyHashCode(k);
  }
  private /*@Nullable*/ WeakKey create(K k) {
      if (k == null) return null;
      else return new WeakKey(k);
  }
  private WeakKey(K k, ReferenceQueue super K> q) {
      super(k, q);
      hash = keyHashCode(k);
  }
  private /*@Nullable*/ WeakKey create(K k, ReferenceQueue super K> q) {
      if (k == null) return null;
      else return new WeakKey(k, q);
  }
        /* A WeakKey is equal to another WeakKey iff they both refer to objects
     that are, in turn, equal according to their own equals methods */
  /*@Pure*/
  @Override
  public boolean equals(/*@Nullable*/ Object o) {
            if (o == null) return false; // never happens
      if (this == o) return true;
            // This test is illegal because WeakKey is a generic type,
            // so use the getClass hack below instead.
      // if (!(o instanceof WeakKey)) return false;
            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);
  }
  /*@Pure*/
  @Override
  public int hashCode() {
      return hash;
  }
    }
    /* Hash table mapping WeakKeys to values */
    private HashMap hash;
    /* Reference queue for cleared WeakKeys */
    private ReferenceQueue super K> queue = new ReferenceQueue();
    /* Remove all invalidated entries from the map, that is, remove all entries
       whose keys have been discarded.  This method should be invoked once by
       each public mutator in this class.  We don't invoke this method in
       public accessors because that can lead to surprising
       ConcurrentModificationExceptions. */
    @SuppressWarnings("unchecked")
    private void processQueue() {
  WeakKey wk;
  while ((wk = (WeakKey)queue.poll()) != null) { // unchecked cast
      hash.remove(wk);
  }
    }
    /* -- Constructors -- */
    /**
     * Constructs a new, empty WeakHashMap with the given
     * initial capacity and the given load factor.
     *
     * @param  initialCapacity  the initial capacity of the
     *                          WeakHashMap
     *
     * @param  loadFactor       the load factor of the WeakHashMap
     *
     * @throws IllegalArgumentException  If the initial capacity is less than
     *                                   zero, or if the load factor is
     *                                   nonpositive
     */
    public WeakHasherMap(int initialCapacity, float loadFactor) {
  hash = new HashMap(initialCapacity, loadFactor);
    }
    /**
     * Constructs a new, empty WeakHashMap with the given
     * initial capacity and the default load factor, which is
     * 0.75.
     *
     * @param  initialCapacity  the initial capacity of the
     *                          WeakHashMap
     *
     * @throws IllegalArgumentException  If the initial capacity is less than
     *                                   zero
     */
    public WeakHasherMap(int initialCapacity) {
  hash = new HashMap(initialCapacity);
    }
    /**
     * Constructs a new, empty WeakHashMap with the default
     * capacity and the default load factor, which is 0.75.
     */
    public WeakHasherMap() {
  hash = new HashMap();
    }
    /**
     * Constructs a new, empty WeakHashMap with the default
     * capacity and the default load factor, which is 0.75.
     * The WeakHashMap uses the specified hasher for hashing
     * keys and comparing them for equality.
     * @param h the Hasher to use when hashing values for this map
     */
    public WeakHasherMap(Hasher h) {
  hash = new HashMap();
  hasher = h;
    }
    /* -- Simple queries -- */
    /**
     * Returns the number of key-value mappings in this map.
     * Note: In contrast to most implementations of the
     * Map interface, the time required by this operation is
     * linear in the size of the map.
     */
    /*@Pure*/
    @Override
    public int size() {
  return entrySet().size();
    }
    /**
     * Returns true if this map contains no key-value mappings.
     */
    /*@Pure*/
    @Override
    public boolean isEmpty() {
  return entrySet().isEmpty();
    }
    /**
     * Returns true if this map contains a mapping for the
     * specified key.
     *
     * @param   key   the key whose presence in this map is to be tested
     */
    /*@Pure*/
    @Override
    public boolean containsKey(Object key) {
        @SuppressWarnings("unchecked")
        K kkey = (K) key;
  return hash.containsKey(WeakKeyCreate(kkey));
    }
    /* -- Lookup and modification operations -- */
    /**
     * Returns the value to which this map maps the specified key.
     * If this map does not contain a value for this key, then return
     * null.
     *
     * @param  key  the key whose associated value, if any, is to be returned
     */
    /*@Pure*/
    @Override
    public /*@Nullable*/ V get(Object key) {  // type of argument is Object, not K
        @SuppressWarnings("unchecked")
        K kkey = (K) key;
  return hash.get(WeakKeyCreate(kkey));
    }
    /**
     * Updates this map so that the given key maps to the given
     * value.  If the map previously contained a mapping for
     * key then that mapping is replaced and the previous value is
     * returned.
     *
     * @param  key    the key that is to be mapped to the given
     *                value
     * @param  value  the value to which the given key is to be
     *                mapped
     *
     * @return  the previous value to which this key was mapped, or
     *          null if if there was no mapping for the key
     */
    @Override
    public V put(K key, V value) {
  processQueue();
  return hash.put(WeakKeyCreate(key, queue), value);
    }
    /**
     * Removes the mapping for the given key from this map, if
     * present.
     *
     * @param  key  the key whose mapping is to be removed
     *
     * @return  the value to which this key was mapped, or null if
     *          there was no mapping for the key
     */
    @Override
    public V remove(Object key) { // type of argument is Object, not K
  processQueue();
        @SuppressWarnings("unchecked")
        K kkey = (K) key;
  return hash.remove(WeakKeyCreate(kkey));
    }
    /**
     * Removes all mappings from this map.
     */
    @Override
    public void clear() {
  processQueue();
  hash.clear();
    }
    /* -- Views -- */
    /* Internal class for entries */
    // This can't be static, again because of dependence on hasher.
    @SuppressWarnings("TypeParameterShadowing")
    private final class Entry implements Map.Entry {
  private Map.Entry ent;
  private K key;  /* Strong reference to key, so that the GC
           will leave it alone as long as this Entry
           exists */
  Entry(Map.Entry ent, K key) {
      this.ent = ent;
      this.key = key;
  }
  /*@Pure*/
  @Override
  public K getKey() {
      return key;
  }
  /*@Pure*/
  @Override
  public V getValue() {
      return ent.getValue();
  }
  @Override
  public V setValue(V value) {
      return ent.setValue(value);
  }
        /*@Pure*/
        private boolean keyvalEquals(K o1, K o2) {
      return (o1 == null) ? (o2 == null) : keyEquals(o1, o2);
  }
        /*@Pure*/
        private boolean valEquals(V o1, V o2) {
      return (o1 == null) ? (o2 == null) : o1.equals(o2);
  }
        /*@Pure*/
        @SuppressWarnings("NonOverridingEquals")
        public boolean equals(Map.Entry e /* Object o*/) {
            // if (! (o instanceof Map.Entry)) return false;
            // Map.Entry e = (Map.Entry)o;
      return (keyvalEquals(key, e.getKey())
        && valEquals(getValue(), e.getValue()));
  }
  /*@Pure*/
  @Override
  public int hashCode() {
      V v;
      return (((key == null) ? 0 : keyHashCode(key))
        ^ (((v = getValue()) == null) ? 0 : v.hashCode()));
  }
    }
    /* Internal class for entry sets */
    private final class EntrySet extends AbstractSet> {
  Set> hashEntrySet = hash.entrySet();
  @Override
  public Iterator> iterator() {
      return new Iterator>() {
    Iterator> hashIterator = hashEntrySet.iterator();
    Map.Entry next = null;
    @Override
    public boolean hasNext() {
        while (hashIterator.hasNext()) {
      Map.Entry ent = hashIterator.next();
      WeakKey wk = ent.getKey();
      K k = null;
      if ((wk != null) && ((k = wk.get()) == null)) {
          /* Weak key has been cleared by GC */
          continue;
      }
      next = new Entry(ent, k);
      return true;
        }
        return false;
    }
    @Override
    public Map.Entry next() {
        if ((next == null) && !hasNext())
      throw new NoSuchElementException();
        Map.Entry e = next;
        next = null;
        return e;
    }
    @Override
    public void remove() {
        hashIterator.remove();
    }
      };
  }
  /*@Pure*/
  @Override
  public boolean isEmpty() {
      return !(iterator().hasNext());
  }
  /*@Pure*/
  @Override
  public int size() {
      int j = 0;
      for (Iterator> 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 e = (Map.Entry)o; // unchecked cast
      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;
  }
  /*@Pure*/
  @Override
  public int hashCode() {
      int h = 0;
      for (Iterator> i = hashEntrySet.iterator(); i.hasNext(); ) {
    Map.Entry 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;
  }
    }
    private /*@Nullable*/ Set> entrySet = null;
    /**
     * Returns a Set view of the mappings in this map.
     */
    /*@SideEffectFree*/
    @Override
    public Set> entrySet() {
  if (entrySet == null) entrySet = new EntrySet();
  return entrySet;
    }
    // find matching key
    K findKey(Object key) {
      processQueue();
      K kkey = (K) key;
      // TODO: use replacement for HashMap to avoid reflection
      WeakKey wkey = WeakKeyCreate(kkey);
      WeakKey found = hashMap_findKey(hash, wkey);
      return found == null ? null : found.get();
    }
}
// you still need to implement hasNext() and next()
static abstract class IterableIterator implements Iterator, Iterable {
  public Iterator iterator() {
    return this;
  }
  
  public void remove() {
    unsupportedOperation();
  }
}
static class Pair implements Comparable> {
   final public Pair setA(A a){ return a(a); }
public Pair a(A a) { this.a = a; return this; }  final public A getA(){ return a(); }
public A a() { return a; }
 A a;
   final public Pair setB(B b){ return b(b); }
public Pair b(B b) { this.b = b; return this; }  final public B getB(){ return b(); }
public B b() { return b; }
 B b;
  Pair() {}
  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 p) {
    if (p == null) return 1;
    int i = ((Comparable) a).compareTo(p.a);
    if (i != 0) return i;
    return ((Comparable) b).compareTo(p.b);
  }
}
static class Fail extends RuntimeException implements IFieldsToList{
  Object[] objects;
  Fail() {}
  Fail(Object... objects) {
  this.objects = objects;}public Object[] _fieldsToList() { return new Object[] {objects}; }
  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 interface IFieldsToList {
  Object[] _fieldsToList();
}
interface IMultiMap {
  public Set keySet();
  public Collection get(A a);
  public int size();
  public int keyCount();
}
static  Map putAll(Map a, Map extends A,? extends B> b) {
  if (a != null && b != null) a.putAll(b);
  return a;
}
static  MultiMap putAll(MultiMap a, Map extends A,? extends B> b) {
  if (a != null) a.putAll((Map) b);
  return a;
}
static  Map putAll(Map a, Object... b) {
  if (a != null)
    litmap_impl(a, b);
  return a;
}
static  void put(Map map, A a, B b) {
  if (map != null) map.put(a, b);
}
static  void put(List l, int i, A a) {
  if (l != null && i >= 0 && i < l(l)) l.set(i, a);
}
// get purpose 1: access a list/array/map (safer version of x.get(y))
static  A get(List l, int idx) {
  return l != null && idx >= 0 && idx < l(l) ? l.get(idx) : null;
}
// seems to conflict with other signatures
/*static  B get(Map map, A key) {
  ret map != null ? map.get(key) : null;
}*/
static  A get(A[] l, int idx) {
  return idx >= 0 && idx < l(l) ? l[idx] : null;
}
// default to false
static boolean get(boolean[] l, int idx) {
  return idx >= 0 && idx < l(l) ? l[idx] : false;
}
// get purpose 2: access a field by reflection or a map
static 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 Object get_raw(String field, Object o) {
  return get_raw(o, field);
}
static 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 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 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 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 Object get(String field, Object o) {
  return get(o, field);
}
static boolean get(BitSet bs, int idx) {
  return bs != null && bs.get(idx);
}
static boolean nempty(Collection c) {
  return !empty(c);
}
static boolean nempty(CharSequence s) {
  return !empty(s);
}
static boolean nempty(Object[] o) { return !empty(o); }
static boolean nempty(byte[] o) { return !empty(o); }
static boolean nempty(int[] o) { return !empty(o); }
static boolean nempty(BitSet bs) { return !empty(bs); }
static boolean nempty(Map m) {
  return !empty(m);
}
static boolean nempty(Iterator i) {
  return i != null && i.hasNext();
}
static boolean nempty(IMultiMap mm) { return mm != null && mm.size() != 0; }
static boolean nempty(Object o) { return !empty(o); }
static boolean nempty(Rect r) { return r != null && r.w != 0 && r.h != 0; }
static  void remove(List l, int i) {
  if (l != null && i >= 0 && i < l(l))
    l.remove(i);
}
static  void remove(Collection l, A a) {
  if (l != null) l.remove(a);
}
static  B remove(Map map, Object a) {
  return map == null ? null : map.remove(a);
}
static void remove(BitSet bs, int i) {
  bs.clear(i);
}
static Object getOpt(Object o, String field) {
  return getOpt_cached(o, field);
}
static Object getOpt(String field, Object o) {
  return getOpt_cached(o, field);
}
static 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); } }
// access of static fields is not yet optimized
static 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 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  A getAndClear(IVar v) {
  A a = v.get();
  v.set(null);
  return a;
}
static  Set keys(Map map) {
  return map == null ? new HashSet() : map.keySet();
}
// convenience shortcut for keys_gen
static Set keys(Object map) {
  return keys((Map) map);
}
  static  Set keys(IMultiMap mm) {
    return mm.keySet();
  }
static  Set keySet(Map map) {
  return map == null ? new HashSet() : map.keySet();
}
static Set keySet(Object map) {
  return keys((Map) map);
}
  static  Set keySet(MultiMap mm) {
    return mm.keySet();
  }
static  int keysSize(MultiMap mm) {
  return lKeys(mm);
}
static  A reverseGet(List l, int idx) {
  if (l == null || idx < 0) return null;
  int n = l(l);
  return idx < n ? l.get(n-1-idx) : null;
}
static  Map cloneMap(Map map) {
  if (map == null) return new HashMap();
  // assume mutex is equal to map
  synchronized(map) {
    return map instanceof TreeMap ? new TreeMap((TreeMap) map) // copies comparator
      : map instanceof LinkedHashMap ? new LinkedHashMap(map)
      : new HashMap(map);
  }
}
static  List cloneMap(Iterable l, IF1 f) {
  List x = emptyList(l);
  if (l != null) for (A o : cloneList(l))
    x.add(f.get(o));
  return x;
}
static  Collection values(Map map) {
  return map == null ? emptyList() : map.values();
}
// convenience shortcut for values_gen
static Collection values(Object map) {
  return values((Map) map);
}
static  Collection values(MultiMap mm) {
  return mm == null ? emptyList() : concatLists(values(mm.data));
}
static > List allValues(Map map) {
  List out = new ArrayList();
  for (var l : values(map))
    addAll(out, l);
  return out;
}
static  List concatLists(Iterable... lists) {
  List l = new ArrayList();
  if (lists != null) for (Iterable list : lists)
    addAll(l, list);
  return l;
}
static  List concatLists(Collection extends Iterable> lists) {
  List l = new ArrayList();
  if (lists != null) for (Iterable list : lists)
    addAll(l, list);
  return l;
}
static int _hashCode(Object a) {
  return a == null ? 0 : a.hashCode();
}
static Method hashMap_findKey_method;
static  A hashMap_findKey(HashMap map, Object key) { try {
  if (hashMap_findKey_method == null)
    hashMap_findKey_method = findMethodNamed(HashMap.class, "getNode");
  Map.Entry entry = (Map.Entry) hashMap_findKey_method.invoke(map, hashMap_internalHash(key), key);
  // java.util.Map.Entry entry = (java.util.Map.Entry) call(hash, 'getNode, hashMap_internalHash(key), wkey);
  return entry == null ? null : entry.getKey();
} catch (Exception __e) { throw rethrow(__e); } }
static UnsupportedOperationException unsupportedOperation() {
  throw new UnsupportedOperationException();
}
static String a(String noun) {
  if (eq(noun, "")) return "?";
  return ("aeiou".indexOf(noun.charAt(0)) >= 0 ? "an " : "a ") + noun;
}
static String a(String contents, Object... params) {
  return hfulltag("a", contents, params);
}
static String b(Object contents, Object... params) {
  return tag("b", contents, params);
}
static int hashCodeFor(Object a) {
  return a == null ? 0 : a.hashCode();
}
static String joinNemptiesWithColon(String... strings) {
  return joinNempties(": ", strings);
}
static String joinNemptiesWithColon(Collection strings) {
  return joinNempties(": ", strings);
}
static String commaCombine(Object... l) {
  return joinNemptiesWithComma(flattenCollectionsAndArrays(ll(l)));
}
static HashMap litmap(Object... x) {
  HashMap map = new HashMap();
  litmap_impl(map, x);
  return map;
}
static 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 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 Field makeAccessible(Field f) {
  try {
    f.setAccessible(true);
  } catch (Throwable e) {
    // Note: The error reporting only works with Java VM option --illegal-access=deny
    
    vmBus_send("makeAccessible_error", e, f);
    
  }
  return f;
}
static Method makeAccessible(Method m) {
  try {
    m.setAccessible(true);
  } catch (Throwable e) {
    
    vmBus_send("makeAccessible_error", e, m);
    
  }
  return m;
}
static Constructor makeAccessible(Constructor c) {
  try {
    c.setAccessible(true);
  } catch (Throwable e) {
    
    vmBus_send("makeAccessible_error", e, c);
    
  }
  return c;
}
static Object getOptDynOnly(DynamicObject o, String field) {
  if (o == null || o.fieldValues == null) return null;
  return o.fieldValues.get(field);
}
//static final Map> getOpt_cache = newDangerousWeakHashMap(f getOpt_special_init);
static class getOpt_Map extends WeakHashMap {
  getOpt_Map() {
    if (getOpt_special == null) getOpt_special = new HashMap();
    clear();
  }
  
  public void clear() {
    super.clear();
    //print("getOpt clear");
    put(Class.class, getOpt_special);
    put(String.class, getOpt_special);
  }
}
static final Map> getOpt_cache = 
  
  
    _registerDangerousWeakMap(synchroMap(new getOpt_Map()));
  
  
static HashMap getOpt_special; // just a marker
/*static void getOpt_special_init(Map map) {
  map.put(Class.class, getOpt_special);
  map.put(S.class, getOpt_special);
}*/
static Map getOpt_getFieldMap(Object o) {
  Class c = _getClass(o);
  HashMap map = getOpt_cache.get(c);
  if (map == null)
    map = getOpt_makeCache(c);
  return map;
}
static Object getOpt_cached(Object o, String field) { try {
  if (o == null) return null;
  Map map = getOpt_getFieldMap(o);
  if (map == getOpt_special) {
    if (o instanceof Class)
      return getOpt((Class) o, field);
    /*if (o instanceof S)
      ret getOpt(getBot((S) 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); } }
// used internally - we are in synchronized block
static HashMap getOpt_makeCache(Class c) {
  HashMap 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  int lKeys(MultiMap mm) {
  return mm == null ? 0 : mm.keysSize();
}
static  void addAll(Collection c, Iterable b) {
  if (c != null && b != null) for (A a : b) c.add(a);
}
static  boolean addAll(Collection c, Collection b) {
  return c != null && b != null && c.addAll(b);
}
static  boolean addAll(Collection c, B... b) {
  return c != null && b != null && c.addAll(Arrays.asList(b));
}
static  Map addAll(Map a, Map extends A,? extends B> b) {
  if (a != null && b != null) a.putAll(b);
  return a;
}
static  A addAll(A c, Collection extends Component> components) {
  return addComponents(c, components);
}
static  A addAll(A c, Component... components) {
  return addComponents(c, components);
}
// This is a bit rough... finds static and non-static methods.
static 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 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 int hashMap_internalHash(Object key) {
  int h;
  return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
static String hfulltag(String tag) {
  return hfulltag(tag, "");
}
static String hfulltag(String tag, Object contents, Object... params) {
  return hopeningTag(tag, params) + str(contents) + "" + tag + ">";
}
static String tag(String tag) {
  return htag(tag);
}
static String tag(String tag, Object contents, Object... params) {
  return htag(tag, str(contents), params);
}
static String tag(String tag, StringBuilder contents, Object... params) {
  return htag(tag, contents, params);
}
static String tag(String tag, StringBuffer contents, Object... params) {
  return htag(tag, contents, params);
}
static String joinNempties(String sep, Object... strings) {
  return joinStrings(sep, strings);
}
static String joinNempties(String sep, Iterable strings) {
  return joinStrings(sep, strings);
}
static String joinNemptiesWithComma(Object... strings) {
  return joinNempties(", ", strings);
}
static String joinNemptiesWithComma(Iterable strings) {
  return joinNempties(", ", strings);
}
static 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 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 void vmBus_send(String msg) {
  vmBus_send(msg, (Object) null);
}
static void clear(Collection c) {
  if (c != null) c.clear();
}
static void clear(Map map) {
  if (map != null) map.clear();
}
static List _registerDangerousWeakMap_preList;
static  A _registerDangerousWeakMap(A map) {
  return _registerDangerousWeakMap(map, null);
}
static  A _registerDangerousWeakMap(A map, Object init) {
  
  callF(init, map);
  
  if (init instanceof String) {
    final String f =  (String) init;
    init = new VF1