l, int startIndex) {
  return cloneList(subList(l, startIndex));
}
  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 String mainClassNameForClassLoader(ClassLoader cl) {
  return or((String) callOpt(cl, "mainClassName"), "main");
}
static Class loadClassFromClassLoader_orNull(ClassLoader cl, String name) {
  try {
    return cl == null ? null : cl.loadClass(name);
  } catch (ClassNotFoundException e) {
    return null;
  }
}
static 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 long parseLong(String s) {
  if (empty(s)) return 0;
  return Long.parseLong(dropSuffix("L", s));
}
static long parseLong(Object s) {
  return Long.parseLong((String) s);
}
static String dropSuffix(String suffix, String s) {
  return nempty(suffix) && endsWith(s, suffix) ? s.substring(0, l(s)-l(suffix)) : s;
}
/**
 * A class that provides scrolling capabilities to a long menu dropdown or
 * popup menu.  A number of items can optionally be frozen at the top and/or
 * bottom of the menu.
 * 
 * Implementation note:  The default number of items to display
 * at a time is 15, and the default scrolling interval is 125 milliseconds.
 * 
 *
 * @version 1.5.0 04/05/12
 * @author Darryl
 * https://tips4java.wordpress.com/2009/02/01/menu-scroller/
 */
 
static class JMenuScroller {
  VF1 fillMenu;
  private JPopupMenu menu;
  private Component[] menuItems;
  private MenuScrollItem upItem;
  private MenuScrollItem downItem;
  private final MenuScrollListener menuListener = new MenuScrollListener();
  private int scrollCount;
  private int interval;
  private int topFixedCount;
  private int bottomFixedCount;
  private int firstIndex = 0;
  private int keepVisibleIndex = -1;
  /**
   * Registers a menu to be scrolled with the default number of items to
   * display at a time and the default scrolling interval.
   * 
   * @param menu the menu
   * @return the JMenuScroller
   */
  public static JMenuScroller setScrollerFor(JMenu menu) {
    return new JMenuScroller(menu);
  }
  /**
   * Registers a popup menu to be scrolled with the default number of items to
   * display at a time and the default scrolling interval.
   * 
   * @param menu the popup menu
   * @return the JMenuScroller
   */
  public static JMenuScroller setScrollerFor(JPopupMenu menu) {
    return new JMenuScroller(menu);
  }
  /**
   * Registers a menu to be scrolled with the default number of items to
   * display at a time and the specified scrolling interval.
   * 
   * @param menu the menu
   * @param scrollCount the number of items to display at a time
   * @return the JMenuScroller
   * @throws IllegalArgumentException if scrollCount is 0 or negative
   */
  public static JMenuScroller setScrollerFor(JMenu menu, int scrollCount) {
    return new JMenuScroller(menu, scrollCount);
  }
  /**
   * Registers a popup menu to be scrolled with the default number of items to
   * display at a time and the specified scrolling interval.
   * 
   * @param menu the popup menu
   * @param scrollCount the number of items to display at a time
   * @return the JMenuScroller
   * @throws IllegalArgumentException if scrollCount is 0 or negative
   */
  public static JMenuScroller setScrollerFor(JPopupMenu menu, int scrollCount) {
    return new JMenuScroller(menu, scrollCount);
  }
  /**
   * Registers a menu to be scrolled, with the specified number of items to
   * display at a time and the specified scrolling interval.
   * 
   * @param menu the menu
   * @param scrollCount the number of items to be displayed at a time
   * @param interval the scroll interval, in milliseconds
   * @return the JMenuScroller
   * @throws IllegalArgumentException if scrollCount or interval is 0 or negative
   */
  public static JMenuScroller setScrollerFor(JMenu menu, int scrollCount, int interval) {
    return new JMenuScroller(menu, scrollCount, interval);
  }
  /**
   * Registers a popup menu to be scrolled, with the specified number of items to
   * display at a time and the specified scrolling interval.
   * 
   * @param menu the popup menu
   * @param scrollCount the number of items to be displayed at a time
   * @param interval the scroll interval, in milliseconds
   * @return the JMenuScroller
   * @throws IllegalArgumentException if scrollCount or interval is 0 or negative
   */
  public static JMenuScroller setScrollerFor(JPopupMenu menu, int scrollCount, int interval) {
    return new JMenuScroller(menu, scrollCount, interval);
  }
  /**
   * Registers a menu to be scrolled, with the specified number of items
   * to display in the scrolling region, the specified scrolling interval,
   * and the specified numbers of items fixed at the top and bottom of the
   * menu.
   * 
   * @param menu the menu
   * @param scrollCount the number of items to display in the scrolling portion
   * @param interval the scroll interval, in milliseconds
   * @param topFixedCount the number of items to fix at the top.  May be 0.
   * @param bottomFixedCount the number of items to fix at the bottom. May be 0
   * @throws IllegalArgumentException if scrollCount or interval is 0 or
   * negative or if topFixedCount or bottomFixedCount is negative
   * @return the JMenuScroller
   */
  public static JMenuScroller setScrollerFor(JMenu menu, int scrollCount, int interval,
          int topFixedCount, int bottomFixedCount) {
    return new JMenuScroller(menu, scrollCount, interval,
            topFixedCount, bottomFixedCount);
  }
  /**
   * Registers a popup menu to be scrolled, with the specified number of items
   * to display in the scrolling region, the specified scrolling interval,
   * and the specified numbers of items fixed at the top and bottom of the
   * popup menu.
   * 
   * @param menu the popup menu
   * @param scrollCount the number of items to display in the scrolling portion
   * @param interval the scroll interval, in milliseconds
   * @param topFixedCount the number of items to fix at the top.  May be 0
   * @param bottomFixedCount the number of items to fix at the bottom.  May be 0
   * @throws IllegalArgumentException if scrollCount or interval is 0 or
   * negative or if topFixedCount or bottomFixedCount is negative
   * @return the JMenuScroller
   */
  public static JMenuScroller setScrollerFor(JPopupMenu menu, int scrollCount, int interval,
          int topFixedCount, int bottomFixedCount) {
    return new JMenuScroller(menu, scrollCount, interval,
            topFixedCount, bottomFixedCount);
  }
  /**
   * Constructs a JMenuScroller that scrolls a menu with the
   * default number of items to display at a time, and default scrolling
   * interval.
   * 
   * @param menu the menu
   */
  public JMenuScroller(JMenu menu) {
    this(menu, 15);
  }
  /**
   * Constructs a JMenuScroller that scrolls a popup menu with the
   * default number of items to display at a time, and default scrolling
   * interval.
   * 
   * @param menu the popup menu
   */
  public JMenuScroller(JPopupMenu menu) {
    this(menu, 15);
  }
  /**
   * Constructs a JMenuScroller that scrolls a menu with the
   * specified number of items to display at a time, and default scrolling
   * interval.
   * 
   * @param menu the menu
   * @param scrollCount the number of items to display at a time
   * @throws IllegalArgumentException if scrollCount is 0 or negative
   */
  public JMenuScroller(JMenu menu, int scrollCount) {
    this(menu, scrollCount, 150);
  }
  /**
   * Constructs a JMenuScroller that scrolls a popup menu with the
   * specified number of items to display at a time, and default scrolling
   * interval.
   * 
   * @param menu the popup menu
   * @param scrollCount the number of items to display at a time
   * @throws IllegalArgumentException if scrollCount is 0 or negative
   */
  public JMenuScroller(JPopupMenu menu, int scrollCount) {
    this(menu, scrollCount, 150);
  }
  /**
   * Constructs a JMenuScroller that scrolls a menu with the
   * specified number of items to display at a time, and specified scrolling
   * interval.
   * 
   * @param menu the menu
   * @param scrollCount the number of items to display at a time
   * @param interval the scroll interval, in milliseconds
   * @throws IllegalArgumentException if scrollCount or interval is 0 or negative
   */
  public JMenuScroller(JMenu menu, int scrollCount, int interval) {
    this(menu, scrollCount, interval, 0, 0);
  }
  /**
   * Constructs a JMenuScroller that scrolls a popup menu with the
   * specified number of items to display at a time, and specified scrolling
   * interval.
   * 
   * @param menu the popup menu
   * @param scrollCount the number of items to display at a time
   * @param interval the scroll interval, in milliseconds
   * @throws IllegalArgumentException if scrollCount or interval is 0 or negative
   */
  public JMenuScroller(JPopupMenu menu, int scrollCount, int interval) {
    this(menu, scrollCount, interval, 0, 0);
  }
  /**
   * Constructs a JMenuScroller that scrolls a menu with the
   * specified number of items to display in the scrolling region, the
   * specified scrolling interval, and the specified numbers of items fixed at
   * the top and bottom of the menu.
   * 
   * @param menu the menu
   * @param scrollCount the number of items to display in the scrolling portion
   * @param interval the scroll interval, in milliseconds
   * @param topFixedCount the number of items to fix at the top.  May be 0
   * @param bottomFixedCount the number of items to fix at the bottom.  May be 0
   * @throws IllegalArgumentException if scrollCount or interval is 0 or
   * negative or if topFixedCount or bottomFixedCount is negative
   */
  public JMenuScroller(JMenu menu, int scrollCount, int interval,
          int topFixedCount, int bottomFixedCount) {
    this(menu.getPopupMenu(), scrollCount, interval, topFixedCount, bottomFixedCount);
  }
  /**
   * Constructs a JMenuScroller that scrolls a popup menu with the
   * specified number of items to display in the scrolling region, the
   * specified scrolling interval, and the specified numbers of items fixed at
   * the top and bottom of the popup menu.
   * 
   * @param menu the popup menu
   * @param scrollCount the number of items to display in the scrolling portion
   * @param interval the scroll interval, in milliseconds
   * @param topFixedCount the number of items to fix at the top.  May be 0
   * @param bottomFixedCount the number of items to fix at the bottom.  May be 0
   * @throws IllegalArgumentException if scrollCount or interval is 0 or
   * negative or if topFixedCount or bottomFixedCount is negative
   */
  public JMenuScroller(JPopupMenu menu, int scrollCount, int interval,
          int topFixedCount, int bottomFixedCount) {
    if (scrollCount <= 0 || interval <= 0) {
      throw new IllegalArgumentException("scrollCount and interval must be greater than 0");
    }
    if (topFixedCount < 0 || bottomFixedCount < 0) {
      throw new IllegalArgumentException("topFixedCount and bottomFixedCount cannot be negative");
    }
    upItem = new MenuScrollItem(UP, -1);
    downItem = new MenuScrollItem(DOWN, +1);
    setScrollCount(scrollCount);
    setInterval(interval);
    setTopFixedCount(topFixedCount);
    setBottomFixedCount(bottomFixedCount);
    this.menu = menu;
    menu.addPopupMenuListener(menuListener);
  }
  /**
   * Returns the scroll interval in milliseconds
   * 
   * @return the scroll interval in milliseconds
   */
  public int getInterval() {
    return interval;
  }
  /**
   * Sets the scroll interval in milliseconds
   * 
   * @param interval the scroll interval in milliseconds
   * @throws IllegalArgumentException if interval is 0 or negative
   */
  public void setInterval(int interval) {
    if (interval <= 0) {
      throw new IllegalArgumentException("interval must be greater than 0");
    }
    upItem.setInterval(interval);
    downItem.setInterval(interval);
    this.interval = interval;
  }
  /**
   * Returns the number of items in the scrolling portion of the menu.
   *
   * @return the number of items to display at a time
   */
  public int getscrollCount() {
    return scrollCount;
  }
  /**
   * Sets the number of items in the scrolling portion of the menu.
   * 
   * @param scrollCount the number of items to display at a time
   * @throws IllegalArgumentException if scrollCount is 0 or negative
   */
  public void setScrollCount(int scrollCount) {
    if (scrollCount <= 0) {
      throw new IllegalArgumentException("scrollCount must be greater than 0");
    }
    this.scrollCount = scrollCount;
    // XXX the following line closes all menus then this menu is made.
    // That doesn't seem right.
    // MenuSelectionManager.defaultManager().clearSelectedPath();
  }
  /**
   * Returns the number of items fixed at the top of the menu or popup menu.
   * 
   * @return the number of items
   */
  public int getTopFixedCount() {
    return topFixedCount;
  }
  /**
   * Sets the number of items to fix at the top of the menu or popup menu.
   * 
   * @param topFixedCount the number of items
   */
  public void setTopFixedCount(int topFixedCount) {
    if (firstIndex <= topFixedCount) {
      firstIndex = topFixedCount;
    } else {
      firstIndex += (topFixedCount - this.topFixedCount);
    }
    this.topFixedCount = topFixedCount;
  }
  /**
   * Returns the number of items fixed at the bottom of the menu or popup menu.
   * 
   * @return the number of items
   */
  public int getBottomFixedCount() {
    return bottomFixedCount;
  }
  /**
   * Sets the number of items to fix at the bottom of the menu or popup menu.
   * 
   * @param bottomFixedCount the number of items
   */
  public void setBottomFixedCount(int bottomFixedCount) {
    this.bottomFixedCount = bottomFixedCount;
  }
  /**
   * Scrolls the specified item into view each time the menu is opened.  Call this method with
   * null to restore the default behavior, which is to show the menu as it last
   * appeared.
   *
   * @param item the item to keep visible
   * @see #keepVisible(int)
   */
  public void keepVisible(JMenuItem item) {
    if (item == null) {
      keepVisibleIndex = -1;
    } else {
      int index = menu.getComponentIndex(item);
      keepVisibleIndex = index;
    }
  }
  /**
   * Scrolls the item at the specified index into view each time the menu is opened.  Call this
   * method with -1 to restore the default behavior, which is to show the menu as
   * it last appeared.
   *
   * @param index the index of the item to keep visible
   * @see #keepVisible(javax.swing.JMenuItem)
   */
  public void keepVisible(int index) {
    keepVisibleIndex = index;
  }
  /**
   * Removes this JMenuScroller from the associated menu and restores the
   * default behavior of the menu.
   */
  public void dispose() {
    if (menu != null) {
      menu.removePopupMenuListener(menuListener);
      menu = null;
    }
  }
  /**
   * Ensures that the dispose method of this JMenuScroller is
   * called when there are no more refrences to it.
   * 
   * @exception  Throwable if an error occurs.
   * @see JMenuScroller#dispose()
   */
  @Override
  public void finalize() throws Throwable {
    dispose();
  }
  private void refreshMenu() {
    if (menuItems != null && menuItems.length > 0) {
      firstIndex = Math.max(topFixedCount, firstIndex);
      firstIndex = Math.min(menuItems.length - bottomFixedCount - scrollCount, firstIndex);
      upItem.setEnabled(firstIndex > topFixedCount);
      downItem.setEnabled(firstIndex + scrollCount < menuItems.length - bottomFixedCount);
      menu.removeAll();
      for (int i = 0; i < topFixedCount; i++) {
        menu.add(menuItems[i]);
      }
      if (topFixedCount > 0) {
        menu.addSeparator();
      }
      menu.add(upItem);
      for (int i = firstIndex; i < scrollCount + firstIndex; i++) {
        menu.add(menuItems[i]);
      }
      menu.add(downItem);
      if (bottomFixedCount > 0) {
        menu.addSeparator();
      }
      for (int i = menuItems.length - bottomFixedCount; i < menuItems.length; i++) {
        menu.add(menuItems[i]);
      }
      JComponent parent = (JComponent) upItem.getParent();
      parent.revalidate();
      parent.repaint();
    }
  }
  private class MenuScrollListener implements PopupMenuListener {
    @Override
    public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
      if (fillMenu != null) {
        clearPopupMenu(menu);
        callF(fillMenu, menu);
      }
      setMenuItems();
    }
    @Override
    public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
      if (fillMenu != null) clearPopupMenu(menu);
      else restoreMenuItems();
    }
    @Override
    public void popupMenuCanceled(PopupMenuEvent e) {
      if (fillMenu != null) clearPopupMenu(menu);
      else restoreMenuItems();
    }
    private void setMenuItems() {
      menuItems = menu.getComponents();
      if (keepVisibleIndex >= topFixedCount
              && keepVisibleIndex <= menuItems.length - bottomFixedCount
              && (keepVisibleIndex > firstIndex + scrollCount
              || keepVisibleIndex < firstIndex)) {
        firstIndex = Math.min(firstIndex, keepVisibleIndex);
        firstIndex = Math.max(firstIndex, keepVisibleIndex - scrollCount + 1);
      }
      if (menuItems.length > topFixedCount + scrollCount + bottomFixedCount) {
        refreshMenu();
      }
    }
    private void restoreMenuItems() {
      menu.removeAll();
      for (Component component : menuItems) {
        menu.add(component);
      }
    }
  }
  private class MenuScrollTimer extends javax.swing.Timer {
    public MenuScrollTimer(final int increment, int interval) {
      super(interval, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
          firstIndex += increment;
          refreshMenu();
        }
      });
    }
  }
  private class MenuScrollItem extends JMenuItem
          implements ChangeListener {
    private MenuScrollTimer timer;
    public MenuScrollItem(MenuIcon icon, int increment) {
      setIcon(icon);
      setDisabledIcon(icon);
      timer = new MenuScrollTimer(increment, interval);
      addChangeListener(this);
    }
    public void setInterval(int interval) {
      timer.setDelay(interval);
    }
    @Override
    public void stateChanged(ChangeEvent e) {
      if (isArmed() && !timer.isRunning()) {
        timer.start();
      }
      if (!isArmed() && timer.isRunning()) {
        timer.stop();
      }
    }
  }
  static MenuIcon UP = new MenuIcon(9, 1, 9);
  static MenuIcon DOWN = new MenuIcon(1, 9, 1);
    
  private static class MenuIcon implements Icon {
    final int[] xPoints = {1, 5, 9};
    final int[] yPoints;
    MenuIcon(int... yPoints) {
      this.yPoints = yPoints;
    }
    @Override
    public void paintIcon(Component c, Graphics g, int x, int y) {
      Dimension size = c.getSize();
      Graphics g2 = g.create(size.width / 2 - 5, size.height / 2 - 5, 10, 10);
      g2.setColor(Color.GRAY);
      g2.drawPolygon(xPoints, yPoints, 3);
      if (c.isEnabled()) {
        g2.setColor(Color.BLACK);
        g2.fillPolygon(xPoints, yPoints, 3);
      }
      g2.dispose();
    }
    @Override
    public int getIconWidth() {
      return 0;
    }
    @Override
    public int getIconHeight() {
      return 10;
    }
  }
}
// a variant of thread where you can get the Runnable target later.
// Also notes its existence on the VM bus.
// We should use this exclusively instead of Thread.
static class BetterThread extends Thread {
  Runnable target;
  
  BetterThread(Runnable target) {
  this.target = target; _created(); }
  BetterThread(Runnable target, String name) { super(name);
  this.target = target; _created(); }
  
  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); } }
  
  Runnable getTarget() { return target; }
}
// has equals semantics so passing it to onChange() etc is idempotent
static class ChangeTrigger implements Runnable , IFieldsToList{
  ChangeTriggerable target;
  ChangeTrigger() {}
  ChangeTrigger(ChangeTriggerable target) {
  this.target = target;}
  public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + target + ")"; }
public boolean equals(Object o) {
if (!(o instanceof ChangeTrigger)) return false;
    ChangeTrigger __1 =  (ChangeTrigger) o;
    return eq(target, __1.target);
}
  public int hashCode() {
    int h = -1239972920;
    h = boostHashCombine(h, _hashCode(target));
    return h;
  }
  public Object[] _fieldsToList() { return new Object[] {target}; }
  public void run() { try { { if (target != null) target.change(); } } catch (Exception __e) { throw rethrow(__e); } }
}
interface ChangeTriggerable {
  public void change();
}
static class Complex implements IFieldsToList{
  static final String _fieldOrder = "re im";
  double re;
  double im;
  Complex() {}
  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}; }
  double abs() { return sqrt(re*re+im*im); }
  
  double re() { return re; }
  double im() { return im; }
  
  final double angle(){ return phase(); }
double phase() { return Math.atan2(im, re); }
  double fracAngle() { return fracNonNeg(angle()/twoPi()); } // angle as 0 to 1
  
  public String toString() {
    if (im != 0)
      return re == 0 ? im + "i" : re + plusPrefixUnlessMinus(str(im)) + "i";
    else
      return str(re);
  }
}
static void clearPopupMenu(final JPopupMenu menu) {
  if (menu != null) { swing(() -> { menu.removeAll(); }); }
}
static String shortClassName_dropNumberPrefix(Object o) {
  return dropNumberPrefix(shortClassName(o));
}
static double fracNonNeg(double d) {
  return frac_nonNeg(d);
}
static double twoPi() {
  return Math.PI*2;
}
static String plusPrefixUnlessMinus(String s) {
  return startsWith(s, "-") ? s : "+" + s;
}
static String dropNumberPrefix(String s) {
  return dropFirst(s, indexOfNonDigit(s));
}
static double frac_nonNeg(double d) {
  return mod(d, 1);
}
static String[] dropFirst(int n, String[] a) {
  return drop(n, a);
}
static String[] dropFirst(String[] a) {
  return drop(1, a);
}
static Object[] dropFirst(Object[] a) {
  return drop(1, a);
}
static  List dropFirst(List l) {
  return dropFirst(1, l);
}
static  List dropFirst(int n, Iterable i) { return dropFirst(n, toList(i)); }
static  List dropFirst(Iterable i) { return dropFirst(toList(i)); }
static  List dropFirst(int n, List l) {
  return n <= 0 ? l : new ArrayList(l.subList(Math.min(n, l.size()), l.size()));
}
static  List dropFirst(List l, int n) {
  return dropFirst(n, l);
}
static String dropFirst(int n, String s) { return substring(s, n); }
static String dropFirst(String s, int n) { return substring(s, n); }
static String dropFirst(String s) { return substring(s, 1); }
static int indexOfNonDigit(String s) {
  int n = l(s);
  for (int i = 0; i < n; i++)
    if (!isDigit(s.charAt(i)))
      return i;
  return -1;
}
// better modulo that gives positive numbers always
static int mod(int n, int m) {
  return (n % m + m) % m;
}
static long mod(long n, long m) {
  return (n % m + m) % m;
}
static BigInteger mod(BigInteger n, int m) {
  return n.mod(bigint(m));
}
static double mod(double n, double m) {
  return (n % m + m) % m;
}
static 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 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 boolean isDigit(char c) {
  return Character.isDigit(c);
}
static BigInteger bigint(String s) {
  return new BigInteger(s);
}
static BigInteger bigint(long l) {
  return BigInteger.valueOf(l);
}
}