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 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 javax.imageio.*;
import java.math.*;
import x30_pkg.x30_util;
import static x30_pkg.x30_util.VF1;
import static x30_pkg.x30_util.l;
import static x30_pkg.x30_util.fail;
import static x30_pkg.x30_util.indexOf;
import static x30_pkg.x30_util.getOpt;
import static x30_pkg.x30_util.setOpt;
import static x30_pkg.x30_util.callOpt;
import static x30_pkg.x30_util.newWeakHashMap;
import static x30_pkg.x30_util.newDangerousWeakHashMap;
import static x30_pkg.x30_util.get;
import static x30_pkg.x30_util.get_raw;
import static x30_pkg.x30_util.assertTrue;
import static x30_pkg.x30_util.isHeadless;
import static x30_pkg.x30_util.isAndroid;
import static x30_pkg.x30_util.isTrue;
import static x30_pkg.x30_util.asList;
import x30_pkg.x30_util.DynamicObject;
import loadableUtils.utils;
import static loadableUtils.utils._threadInfo;
import static loadableUtils.utils._threadInheritInfo;
import static loadableUtils.utils._threadInfo_addMakerAndRetriever;
import static loadableUtils.utils.dm_currentModule;
import static loadableUtils.utils.dm_current_mandatory;
import static loadableUtils.utils.match;
import static loadableUtils.utils.getOpt_raw;
import static loadableUtils.utils.setOpt_raw;
import static loadableUtils.utils.getField;
import static loadableUtils.utils.fieldType;
import static loadableUtils.utils.format3;
import static loadableUtils.utils.vm_generalIdentityHashSet;
import static loadableUtils.utils.vm_generalHashMap;
import static loadableUtils.utils.vm_generalWeakSubMap;
import static loadableUtils.utils.bindToComponent;
import static loadableUtils.utils.struct;
import static loadableUtils.utils.structure;
import static loadableUtils.utils.loadPage;
import static loadableUtils.utils.loadPage_utf8;
import static loadableUtils.utils.loadPageSilentlyWithTimeout;
import static loadableUtils.utils.loadPageSilently;
import static loadableUtils.utils.loadSnippet;
import static loadableUtils.utils.loadSnippetQuietly;
import static loadableUtils.utils.sendToLocalBot;
import static loadableUtils.utils.componentPopupMenu;
import static loadableUtils.utils.componentPopupMenu_top;
import static loadableUtils.utils.componentPopupMenu_getEvent;
import static loadableUtils.utils.listPopupMenu;
import static loadableUtils.utils.tablePopupMenu;
import static loadableUtils.utils.sexyTableWithoutDrag;
import static loadableUtils.utils.dm_current_generic;
import static loadableUtils.utils.dm_current_mandatory_generic;
import static loadableUtils.utils.cset;
import static loadableUtils.utils.DynamicObject_loading;
import static loadableUtils.utils.concepts_unlisted;
import static loadableUtils.utils.makePopupMenuConditional;
import static loadableUtils.utils.makeConceptsTable_idWidth;
import static loadableUtils.utils.showConceptsTable_afterUpdate;
import static loadableUtils.utils.dynamicObjectIsLoading;
import loadableUtils.utils.F0;
import loadableUtils.utils.F1;
import loadableUtils.utils.Matches;
import loadableUtils.utils.BetterLabel;
import loadableUtils.utils.SingleComponentPanel;
import loadableUtils.utils.Snippet;
import loadableUtils.utils.Q;
import loadableUtils.utils.ImageSurface;
import loadableUtils.utils.structure_Data;
import loadableUtils.utils.RGBImage;
import loadableUtils.utils.RGB;
import loadableUtils.utils.BWImage;
import loadableUtils.utils.MakesBufferedImage;
import loadableUtils.utils.Concept;
import loadableUtils.utils.Concepts;
import loadableUtils.utils.IConceptIndex;
import loadableUtils.utils.IFieldIndex;
import loadableUtils.utils.Derefable;
import loadableUtils.utils.ImageSurfaceSelector;
import loadableUtils.utils.SimpleCRUD;
import loadableUtils.utils.PersistableThrowable;
import loadableUtils.utils.DynModule;
import loadableUtils.utils.DynPrintLog;
import loadableUtils.utils.DynObjectTable;
import loadableUtils.utils.DynImageSurface;
import loadableUtils.utils.DynCalculatedList;
import net.dv8tion.jda.core.*;
import net.dv8tion.jda.core.entities.*;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.hooks.*;
import net.dv8tion.jda.core.events.message.*;
import net.dv8tion.jda.core.events.message.react.*;
import java.text.*;
import java.text.NumberFormat;
import javax.swing.undo.UndoManager;
import javax.net.ssl.*;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.nio.file.Files;
import java.nio.file.Path;

class main {

  public static class DiscordBot extends DynPrintLogAndEnabled {

    public transient JDA bot;

    public transient Set<Long> msgsReactedTo = new HashSet();

    public transient double deleteDelay = 60.0;

    public transient boolean doDelete;

    public transient int maxMonologue = 5;

    public transient boolean selfTalk = false;

    public transient Color imageEmbedMysteriousLineColor = colorFromHex("36393f");

    public transient InheritableThreadLocal<MessageChannel> currentChannel = new InheritableThreadLocal();

    public transient InheritableThreadLocal<Message> respondingTo = new InheritableThreadLocal();

    public transient List<Long> lastPosted = new ArrayList();

    public transient GazelleContextCache_v2 contextCache;

    public transient Set<String> acceptablePurposes = litciset("");

    public void start() {
      super.start();
      vm_cleanPrints();
      logModuleOutput();
      dm_useLocalMechListCopies();
      print("Making cache");
      long _startTime_12 = sysNow();
      contextCache = new GazelleContextCache_v2(this);
      ownResource(contextCache.listenToMessages());
      contextCache.fill();
      done2_always("Made cache", _startTime_12);
      bot = discordBot(new ListenerAdapter() {

        public void onMessageUpdate(MessageUpdateEvent e) {
          try {
            AutoCloseable __6 = enter();
            try {
              {
                if (!enabled || !licensed())
                  return;
              }
              long msgID = e.getMessage().getIdLong();
              String content = e.getMessage().getContentRaw();
              Object lineConcept = dm_discord_lineForMsgID_unimported(msgID);
              String rendered = msgID + ": " + content;
              if (lineConcept == null) {
                print("Weird: Message edited, but not found: " + rendered);
                return;
              }
              call(lineConcept, "_setField", "editedText", content);
              print("Message edited: " + rendered);
            } finally {
              _close(__6);
            }
          } catch (Throwable __e) {
            _handleException(__e);
          }
        }

        public void onMessageReceived(MessageReceivedEvent e) {
          try {
            AutoCloseable __7 = enter();
            try {
              {
                if (!enabled || !licensed())
                  return;
              }
              boolean bot = e.getAuthor().isBot();
              long msgID = e.getMessage().getIdLong();
              long userID = e.getAuthor().getIdLong();
              Member member = e.getMember();
              String userName = member == null ? null : member.getNickname();
              if (userName == null && member != null)
                userName = member.getEffectiveName();
              if (userName == null)
                userName = e.getAuthor().getName();
              final Message msg = e.getMessage();
              print("Channel type: " + e.getChannelType());
              boolean isPrivate = e.getChannelType() == ChannelType.PRIVATE;
              String content = trim(msg.getContentRaw());
              print("Msg from " + userName + ": " + content);
              if (empty(content))
                return;
              final String originalContent = content;
              Object user = userConcept(e.getAuthor());
              String crud = dm_gazelle_linesCRUD();
              Object channel = dm_call(crud, "uniqChannel", msg.getChannel().getIdLong());
              dm_call(crud, "cset", channel, litobjectarray("name", msg.getChannel().getName()));
              Object lineConcept = dm_call(crud, "uniqConcept", litObjectArrayAsObject("msgID", msgID));
              dm_call(crud, "cset", lineConcept, litobjectarray("text", content, "bot", bot, "isPrivate", isPrivate, "author", user, "channel", channel));
              vmBus_send("newDiscordLine", lineConcept);
              Request request = new Request();
              answer(request);
            } finally {
              _close(__7);
            }
          } catch (Throwable __e) {
            _handleException(__e);
          }
        }

        public void onMessageReactionAdd(MessageReactionAddEvent e) {
          try {
            AutoCloseable __8 = enter();
            try {
              {
                if (!enabled || !licensed())
                  return;
              }
              MessageReaction r = e.getReaction();
              boolean bot = e.getUser().isBot();
              long msgID = r.getMessageIdLong();
              add(msgsReactedTo, msgID);
              print("User " + e.getUser() + (bot ? " (bot)" : "") + " reacted to message " + msgID + " with " + r.getReactionEmote());
              if (bot)
                return;
              String crud = dm_gazelle_linesCRUD();
              Object lineConcept = dm_call(crud, "uniqConcept", litObjectArrayAsObject("msgID", msgID));
              List reactions = (List) (get(lineConcept, "reactions"));
              print("lineConcept=" + lineConcept);
              print("reactions=" + reactions);
              Object reaction = dm_call(crud, "nuReaction", litObjectArrayAsObject("user", userConcept(e.getUser()), "emoji", r.getReactionEmote().getName(), "created", now()));
              print("reaction=" + reaction);
              dm_call(crud, "cset", lineConcept, litobjectarray("reactions", listPlus(reactions, reaction)));
              dm_discord_gatherFeedbackFromLine(dm_discord_importLine(lineConcept));
            } finally {
              _close(__8);
            }
          } catch (Throwable __e) {
            _handleException(__e);
          }
        }
      });
      dm_registerAs("discordBot");
      print("Started bot");
    }

    public class Request {
    }

    public void answer(Request request) {
      final MessageChannel ch = e.getChannel();
      long channelID = ch.getIdLong();
      List<GazelleLine> linesInChannel = dm_discord_linesInChannel(channelID);
      if (bot) {
        int monologueLength = (int) (dm_call(dm_gazelle_linesCRUD(), "monologueLength", channelID));
        if (monologueLength >= maxMonologue)
          return;
      }
      AutoCloseable __9 = tempSetTL(currentChannel, ch);
      try {
        AutoCloseable __10 = tempSetTL(respondingTo, msg);
        try {
          String userForModulesManager = "discord user " + userID;
          if (!bot) {
            for (T3<String, String, String> t : dm_gazelle_allRulesWithPurpose("moduleMeta")) {
              Pair<String, String> p = splitAtDoubleArrow_pair(t.a);
              if (eqic(p.b, "stop module") && nempty(p.a) && matchX2(p.a, content)) {
                print("Stopping modules");
                postText((String) dm_call(dm_gazelle_modulesManager(), "deleteAllModulesForUser", userForModulesManager, "silentIfEmpty", true));
              }
            }
          }
          Matches m = new Matches();
          try {
            if (swicOneOf(content, "!eval ", "!fresh ", "!real-eval", "!safe-eval ") || eqic(content, "!fresh")) {
              Object safetyCheck = null;
              boolean authed = dm_discord_userCanEval(userID);
              if (swic_trim(content, "!safe-eval ", m)) {
                content = "!eval " + m.rest();
                authed = false;
              }
              if (regexpMatches("![a-z\\-]+\\s+again", content)) {
                GazelleLine last = dm_discord_nextToLastEvalByUser(userID);
                if (last == null) {
                  postInChannel(ch, "No last eval found");
                  return;
                }
                content = replaceSuffix("again", afterSpace(last.text), content);
              }
              if (!authed)
                safetyCheck = botEval_strictSafetyCheck();
              sendTyping();
              {
                dm_bot_execEvalCmd(new VF1<String>() {

                  public void get(String s) {
                    try {
                      if (s != null)
                        postInChannel(ch, shorten(ifEmpty(s, "\"\""), 1000));
                    } catch (Exception __e) {
                      throw rethrow(__e);
                    }
                  }

                  public String toString() {
                    return "if (s != null)\r\n            postInChannel(ch, shorten(ifEmpty(s, [[\"\"]]), 1000))";
                  }
                }, content, "safetyCheck", safetyCheck);
                return;
              }
            }
            if (eqic(content, "!rule")) {
              List<String> lines = linesInChannelBy(channelID, userID);
              if (contains(nextToLast(lines), "=>"))
                content = "!rule " + nextToLast(lines);
              else {
                if (l(lines) < 3)
                  throw fail("Too few lines");
                content = "!rule " + nextToNextToLast(lines) + " => " + nextToLast(lines);
              }
            }
            if (swicOneOf(content, m, "!rule ", "!rule\n")) {
              String s = trim(m.rest());
              print("Processing rule: " + s);
              String opt = leadingSquareBracketStuff(s);
              s = dropActuallyLeadingSquareBracketStuff(s);
              if (startsWith(s, "=>"))
                s = assertNotNull("No last line in channel", nextToLast(linesInChannelBy(channelID, userID))) + "\n" + s;
              List<String> comments = ll("made by user", "discord", "tokenize out with javaTok");
              if (cic(pairB(tok_splitAtDoubleArrow_pair(s)), userName)) {
                s = optCurly(userName) + " says: " + s;
                comments.add("with user name");
              }
              gazelle_parsePublicRuleOptions(opt, comments);
              s = replace(s, " + ", "\n+ ");
              s = jreplace1(s, "=>", "\n=>");
              s = gazelle_processSquareBracketAnnotations(s, comments);
              AutoCloseable __11 = tempSetTL(dm_gazelle_addRuleWithComment_renderWithoutComments, true);
              try {
                dm_gazelle_addRuleWithComment(s, lines_rtrim(comments));
                {
                  postText(dm_gazelle_addRuleWithComment_msg.get());
                  return;
                }
              } finally {
                _close(__11);
              }
            }
            if (swic_trim(content, "!phrase ", m)) {
              gazelle_createRuleForPhrase(m.rest());
              {
                postText(dm_gazelle_addRuleWithComment_msg.get());
                return;
              }
            }
            if (bot && !selfTalk)
              return;
            String purpose = null;
            boolean debug = false, skipBad = true;
            Set<String> restrictToRules = null;
            boolean change;
            do {
              change = false;
              if (swic_trim(content, "!withBad ", m)) {
                skipBad = false;
                content = m.rest();
                change = true;
              }
              if (swic_trim(content, "!preprocess ", m)) {
                purpose = "preprocess";
                content = m.rest();
                change = true;
              }
              if (swic_trim(content, "!debug ", m)) {
                debug = true;
                content = m.rest();
                change = true;
              }
              if (swic(content, "!using[", m)) {
                int i = indexOf(m.rest(), ']');
                restrictToRules = litset(substring(m.rest(), 0, i));
                content = trimSubstring(m.rest(), i + 1);
              }
            } while (change);
            print("debug=" + debug + ", content=" + content);
            List<String> preContext = takeLast(2, dropLast(collect("text", linesInChannel)));
            print("preContext=" + preContext);
            GazelleContextCache_v2 contextCache = DiscordBot.this.contextCache;
            if (restrictToRules != null) {
              contextCache = gazelle_cloneContextCache(contextCache);
              Set<String> _restrictToRules = restrictToRules;
              print("restrictToRules=" + restrictToRules);
              contextCache.cachedCtx.engine.dropRulesWhere(r -> !contains(_restrictToRules, r.globalID));
              print("Using rules: " + collect("globalID", contextCache.cachedCtx.engine.rules));
            }
            Object[] params = litmapparams("userName", userName, "skipBad", skipBad, "preContext", preContext, "badComments", mechCISet("Knock-out rule comments"), "acceptablePurposes", nempty(purpose) ? litciset(purpose) : acceptablePurposes, "respondingToHuman", !bot, "debug", debug, "userID", userID, "contextMaker", contextCache, "debugPreprocessing", true, "sendToModules", new F1<String, List<GazelleTree>>() {

              public List<GazelleTree> get(String input) {
                try {
                  return gazelle_wrapLinesAsTree(gazelle_answersFromModules(userForModulesManager, input));
                } catch (Exception __e) {
                  throw rethrow(__e);
                }
              }

              public String toString() {
                return "gazelle_wrapLinesAsTree(gazelle_answersFromModules(userForModulesManager, inp...";
              }
            });
            if (eq(purpose, "preprocess")) {
              postText(lines_rtrim(takeFirst(10, gazelle_preprocess(content, params))));
              return;
            }
            List<GazelleTree> l;
            if (nempty(purpose))
              l = gazelle_postprocess(dm_gazelle_reasonAboutChatInput_v2(userName, content, params));
            else
              l = gazelle_reason_repeat(content, params);
            int idx = 0;
            for (final GazelleTree t : l) {
              final int _idx = idx++;
              if (eqic(t.lineType, "temporary fact")) {
                if (dm_gazelle_hasTempFact(t.line))
                  continue;
                print("Saving temp fact: " + t.line);
                dm_gazelle_addTempFact(t.line, "discord msg " + msgID);
              }
              if (eqic(t.lineType, "eval")) {
                String code = t.rule().out;
                if (!mechSet("Gazelle | Allowed Evals").contains(code))
                  print("Eval not allowed: " + code);
                else {
                  sendTyping();
                  String out = strOrNull(dm_javaEvalOrInterpret(code));
                  if (nempty(out))
                    postInChannelWithDelete(ch, out, new VF1<Message>() {

                      public void get(Message msg2) {
                        try {
                          Gazelle_ReasoningForLine reasoning = nu(Gazelle_ReasoningForLine.class, "outMsgID", msg2.getIdLong(), "outText", out, "inMsgID", msg.getIdLong(), "inUserID", userID, "inChannelID", channelID, "inText", originalContent, "tree", t, "treeIndex", _idx);
                          dm_gazelle_saveReasoningForLine(reasoning);
                        } catch (Exception __e) {
                          throw rethrow(__e);
                        }
                      }

                      public String toString() {
                        return "Gazelle_ReasoningForLine reasoning = nu(Gazelle_ReasoningForLine,\r\n          ...";
                      }
                    });
                }
                continue;
              }
              gazelle_storeRecentlyUsedMappings(ll(t), "context", "discord channel " + channelID);
              final String out = tok_dropCurlyBrackets(t.line);
              print(">> " + t);
              print("POSTING: " + out);
              postInChannelWithDelete(ch, out, new VF1<Message>() {

                public void get(Message msg2) {
                  try {
                    Gazelle_ReasoningForLine reasoning = nu(Gazelle_ReasoningForLine.class, "outMsgID", msg2.getIdLong(), "outText", out, "inMsgID", msg.getIdLong(), "inUserID", userID, "inChannelID", channelID, "inText", originalContent, "tree", t, "treeIndex", _idx);
                    dm_gazelle_saveReasoningForLine(reasoning);
                  } catch (Exception __e) {
                    throw rethrow(__e);
                  }
                }

                public String toString() {
                  return "Gazelle_ReasoningForLine reasoning = nu(Gazelle_ReasoningForLine,\r\n          ...";
                }
              });
            }
          } catch (Throwable error) {
            _handleException(error);
            postInChannel(ch, exceptionToStringShort(error));
          }
        } finally {
          _close(__10);
        }
      } finally {
        _close(__9);
      }
    }

    public void cleanMeUp() {
      if (bot != null) {
        try {
          print("Shutting down bot");
          bot.shutdown();
          print("Bot shut down");
        } catch (Throwable __e) {
          _handleException(__e);
        }
      }
      bot = null;
    }

    public Object userConcept(User user) {
      String crud = dm_gazelle_linesCRUD();
      Object userConcept = dm_call(crud, "uniqUser", user.getIdLong());
      dm_call(crud, "cset", userConcept, litobjectarray("name", user.getName()));
      return userConcept;
    }

    public MessageChannel getChannel(long channelID) {
      return bot.getTextChannelById(channelID);
    }

    public void postInChannel(long channelID, String msg) {
      postInChannel(getChannel(channelID), msg);
    }

    public void postInChannel(MessageChannel channel, String msg) {
      String postID = (String) (dm_call(gazelle_postedLinesCRUD(), "postingLine", channel.getId(), msg));
      channel.sendMessage(msg).queue(m -> {
        dm_call(gazelle_postedLinesCRUD(), "donePosting", postID, "discord msg " + m.getId());
      });
      if (l(lastPosted) > 5)
        popFirst(lastPosted);
      lastPosted.add(now());
    }

    public void postInChannel(String channel, String msg) {
      long id = dm_discord_channelID(channel);
      if (id == 0)
        throw fail("Channel not found: " + channel);
      postInChannel(id, msg);
    }

    public void postInChannelWithDelete(MessageChannel channel, String msg, VF1<Message> onPost) {
      String postID = (String) (dm_call(gazelle_postedLinesCRUD(), "postingLine", channel.getId(), msg));
      channel.sendMessage(msg).queue(msg2 -> {
        dm_pcall(gazelle_postedLinesCRUD(), "donePosting", postID, "discord msg " + msg2.getId());
        pcallF(onPost, msg2);
        final long msgId = msg2.getIdLong();
        print("I sent msg: " + msgId);
        if (doDelete)
          doLater(deleteDelay, new Runnable() {

            public void run() {
              try {
                {
                  if (contains(msgsReactedTo, msgId))
                    return;
                }
                print("Deleting msg " + msgId);
                msg2.delete().queue();
              } catch (Exception __e) {
                throw rethrow(__e);
              }
            }

            public String toString() {
              return "return if contains(msgsReactedTo, msgId);\r\n        print(\"Deleting msg \" + ms...";
            }
          });
      }, error -> _handleException(error));
    }

    public void postText(String text) {
      postInChannel(currentChannel.get(), text);
    }

    public void postImage(String url) {
      postImage(currentChannel.get(), url);
    }

    public void postImage(String url, String title) {
      postImage(currentChannel.get(), url, title);
    }

    public void postImage(MessageChannel channel, String url) {
      postImage(channel, url, "");
    }

    public void postImage(MessageChannel channel, String url, String description) {
      channel.sendMessage(new EmbedBuilder().setImage(absoluteURL(url)).setDescription(unnull(description)).setColor(imageEmbedMysteriousLineColor).build()).queue();
    }

    public List<String> linesInChannelBy(long channelID, long userID) {
      return collect("text", dm_discord_linesInChannelBy(channelID, userID));
    }

    public MessageChannel currentChannel() {
      return currentChannel.get();
    }

    public Message respondingTo() {
      return respondingTo.get();
    }

    public void sendTyping() {
      currentChannel().sendTyping().queue();
    }

    public List<GazelleTree> reasonAbout(String line, Object... __) {
      return gazelle_reasonAbout(line, paramsPlus(__, "ctx", contextCache.get()));
    }

    public void rebuildCache() {
      contextCache.fill();
    }

    public void editMessage(long channelID, long msgID, String text) {
      getChannel(channelID).editMessageById(str(msgID), text).queue();
    }

    public String respondingToUserID() {
      return respondingTo.get() == null ? null : "discord user " + respondingTo.get().getAuthor().getIdLong();
    }
  }

  public static boolean _moduleClass_DiscordBot;

  public static Object callF(Object f, Object... args) {
    try {
      if (f instanceof String)
        return callMC((String) f, args);
      return x30_util.callF(f, args);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

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

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

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

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

  public static Object callMC(String method, Object... args) {
    return call(mc(), method, args);
  }

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

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

  public static void _onLoad_initUtils() {
    utils.__javax = javax();
  }

  public static void _onLoad_defaultClassFinder() {
    setDefaultClassFinder(new F1<String, Class>() {

      public Class get(String name) {
        try {
          Class c = findClass_fullName(name);
          if (c != null)
            return c;
          if (startsWith(name, "main$"))
            return loadableUtils.utils.findClass_fullName("loadableUtils.utils" + substring(name, 4));
          return null;
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "Class c = findClass_fullName(name);\r\n    if (c != null) ret c;\r\n    if (start...";
      }
    });
  }

  public static String programID() {
    return getProgramID();
  }

  public static String programID(Object o) {
    return getProgramID(o);
  }

  public static volatile StringBuffer local_log = new StringBuffer();

  public static volatile Appendable print_log = local_log;

  public static volatile int print_log_max = 1024 * 1024;

  public static volatile int local_log_max = 100 * 1024;

  public static boolean print_silent;

  public static Object print_byThread_lock = new Object();

  public static volatile ThreadLocal<Object> print_byThread;

  public static volatile Object print_allThreads;

  public static volatile Object print_preprocess;

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

  public static <A> A print(String s, A o) {
    print((endsWithLetterOrDigit(s) ? s + ": " : s) + o);
    return o;
  }

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

  public static void print_noNewLine(String s) {
    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;
    print_raw(s);
  }

  public static 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);
    System.out.print(s);
  }

  public static java.awt.Color colorFromHex(String hex) {
    return awtColor(hex);
  }

  public static TreeSet<String> litciset(String... items) {
    TreeSet<String> set = caseInsensitiveSet();
    for (String a : items) set.add(a);
    return set;
  }

  public static void vm_cleanPrints() {
    print_preprocess = new F1<String, String>() {

      public String get(String x) {
        try {
          return vm_cleanForPrint(x);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "vm_cleanForPrint(x)";
      }
    };
  }

  public static boolean logModuleOutput_installed;

  public static void logModuleOutput() {
    if (logModuleOutput_installed)
      return;
    logModuleOutput_installed = true;
    final File logFile = getProgramFile("output.txt");
    final LineBuffer buf = new LineBuffer(new VF1<String>() {

      public void get(String line) {
        try {
          appendToTextFile(logFile, "[" + localDateWithMilliseconds() + "] " + line + "\n");
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "appendToTextFile(logFile, \"[\" + localDateWithMilliseconds() + \"] \" + line + \"...";
      }
    });
    print_forAllThreads(new VF1<String>() {

      public void get(String s) {
        try {
          buf.append(s);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "buf.append(s);";
      }
    });
  }

  public static void dm_useLocalMechListCopies() {
    dm_useLocallyCopiedMechLists();
  }

  public static long sysNow() {
    ping();
    return System.nanoTime() / 1000000;
  }

  public static void ownResource(AutoCloseable c) {
    _registerAutoCloseable(c);
  }

  public static long done2_always(long startTime, String desc) {
    long time = sysNow() - startTime;
    print(desc + " [" + time + " ms]");
    return time;
  }

  public static long done2_always(String desc, long startTime) {
    return done2_always(startTime, desc);
  }

  public static long done2_always(long startTime) {
    return done2_always(startTime, "");
  }

  public static JDA discordBot(final VF1<MessageReceivedEvent> onMessage) {
    return discordBot(new ListenerAdapter() {

      @Override
      public void onMessageReceived(MessageReceivedEvent e) {
        if (e.getAuthor().isBot()) {
          print("  Msg from bot, skipping");
          return;
        }
        pcallF(onMessage, e);
      }
    });
  }

  public static JDA discordBot(ListenerAdapter listener) {
    try {
      JDABuilder builder = new JDABuilder(AccountType.BOT);
      File tokenFile = javaxSecretDir("discord-bot-token");
      builder.setToken(assertNempty("Need: " + tokenFile, trim(loadTextFile(tokenFile))));
      builder.addEventListener(listener);
      return builder.build();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static volatile boolean licensed_yes = true;

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

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

  public static Object dm_discord_lineForMsgID_unimported(long msgID) {
    return dm_call(dm_gazelle_linesCRUD(), "lineForID", msgID);
  }

  public static 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);
      }
  }

  public static volatile PersistableThrowable _handleException_lastException;

  public static List _handleException_onException = synchroList(ll("printStackTrace2"));

  public static 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)
      return;
    for (Object f : cloneList(_handleException_onException)) try {
      callF(f, e);
    } catch (Throwable e3) {
      printStackTrace2(e3);
    }
  }

  public static String trim(String s) {
    return s == null ? null : s.trim();
  }

  public static String trim(StringBuilder buf) {
    return buf.toString().trim();
  }

  public static String trim(StringBuffer buf) {
    return buf.toString().trim();
  }

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

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

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

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

  public 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));
  }

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

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

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

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

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

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

  public static String dm_gazelle_linesCRUD() {
    return dm_require("#1021628/GLines");
  }

  public static Object dm_call(Object moduleOrID, String method, Object... args) {
    Object mod = dm_getModule(moduleOrID);
    if (mod == null)
      return null;
    AutoCloseable __134 = dm_enter(mod);
    try {
      return call_withVarargs(mod, method, args);
    } finally {
      _close(__134);
    }
  }

  public static Object[] litobjectarray(Object... l) {
    return litObjectArray(l);
  }

  public static Object litObjectArrayAsObject(Object... l) {
    return litobjarrayAsObject(l);
  }

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

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

  public static void add(BitSet bs, int i) {
    bs.set(i);
  }

  public static <A> boolean add(Collection<A> c, A a) {
    return c != null && c.add(a);
  }

  public static void add(Container c, Component x) {
    addToContainer(c, x);
  }

  public static long now_virtualTime;

  public static long now() {
    return now_virtualTime != 0 ? now_virtualTime : System.currentTimeMillis();
  }

  public static <A> List<A> listPlus(Collection<A> l, A... more) {
    return concatLists(l, asList(more));
  }

  public static void dm_discord_gatherFeedbackFromLine(GazelleLine l) {
    try {
      String mrStruct = gazelle_discord_matchedRuleStructForMsgID(l.msgID);
      if (mrStruct == null)
        return;
      print("Processing: " + l.msgID + " - " + l.text);
      printIndent(shorten(mrStruct, 50));
      printIndent(l.reactions);
      List<String> posNeg = uniquifyCI(map("dm_posNegStringForEmoji", collect("emoji", l.reactions)));
      printIndent("posNeg = " + posNeg);
      if (l(posNeg) > 1) {
        warn("  Mixed feedback, skipping");
        return;
      }
      String judgement = cic(posNeg, "positive") ? "good" : "bad";
      dm_gazelle_saveAppliedRule("judgement", judgement, "matchedRuleStruct", mrStruct, "context", "discord msg " + l.msgID);
    } catch (Throwable __e) {
      _handleException(__e);
    }
  }

  public static GazelleLine dm_discord_importLine(Object lineConcept) {
    return lineConcept == null ? null : nu(GazelleLine.class, "timestamp", _get("created", lineConcept), "user", call(lineConcept, "userName"), "bot", _get("bot", lineConcept), "text", _get("text", lineConcept), "msgID", getLong("msgID", lineConcept), "context", _get("context", lineConcept), "reactions", map(getList(lineConcept, "reactions"), new F1<Object, GazelleReaction>() {

      public GazelleReaction get(Object r) {
        try {
          return nu(GazelleReaction.class, "userID", getLong((Object) getOpt("user", r), "userID"), "created", _get("created", r), "emoji", _get("emoji", r));
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "nu GazelleReaction(\r\n        userID := getLong((O) getOpt user(r), 'userID),\r...";
      }
    }));
  }

  public static AutoCloseable dm_registerAs(String... msgs) {
    F0<String> f = new F0<String>() {

      public String get() {
        try {
          return dm_moduleID();
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "ret dm_moduleID();";
      }
    };
    return combineAutoCloseables(map(msgs, msg -> dm_vmBus_answerToMessage(msg, f)));
  }

  public static List<GazelleLine> dm_discord_linesInChannel(long channelID) {
    return map("dm_discord_importLine", (List) dm_call(dm_gazelle_linesCRUD(), "linesInChannel", channelID));
  }

  public static List<GazelleLine> dm_discord_linesInChannel(String channelName) {
    return dm_discord_linesInChannel((long) dm_call(dm_gazelle_linesCRUD(), "idForChannel", channelName));
  }

  public static <A> AutoCloseable tempSetTL(ThreadLocal<A> tl, A a) {
    return tempSetThreadLocal(tl, a);
  }

  public static List<T3<String, String, String>> dm_gazelle_allRulesWithPurpose(String purpose) {
    if (empty(purpose) || eq(purpose, "default"))
      return filter(gazelle_allRuleTriples(), t -> empty(gazelle_purposeFromComments(t.b)));
    else
      return dm_gazelle_allRulesWithComment("purpose: " + purpose);
  }

  public static Pair<String, String> splitAtDoubleArrow_pair(String s) {
    return splitAtDoubleArrow_pair(javaTokWithBrackets(s));
  }

  public static Pair<String, String> splitAtDoubleArrow_pair(List<String> tok) {
    return listToPair(splitAtDoubleArrow(tok));
  }

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

  public static 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);
  }

  public static boolean nempty(Collection c) {
    return !empty(c);
  }

  public static boolean nempty(CharSequence s) {
    return !empty(s);
  }

  public static boolean nempty(Object[] o) {
    return !empty(o);
  }

  public static boolean nempty(byte[] o) {
    return !empty(o);
  }

  public static boolean nempty(int[] o) {
    return !empty(o);
  }

  public static boolean nempty(Map m) {
    return !empty(m);
  }

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

  public static boolean nempty(Object o) {
    return !empty(o);
  }

  public static boolean matchX2(String pat, String s) {
    return matchX_vbar(pat, s);
  }

  public static boolean matchX2(String pat, String s, Matches matches) {
    return matchX_vbar(pat, s, matches);
  }

  public static String dm_gazelle_modulesManager() {
    return dm_require("#1021931/GModulesManager");
  }

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

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

  public static boolean dm_discord_userCanEval(long userID) {
    return (boolean) dm_requireAndCall("#1021656/GUsers", "canEval", userID);
  }

  public static boolean swic_trim(String a, String b, Matches m) {
    if (!swic(a, b))
      return false;
    m.m = new String[] { trim(substring(a, l(b))) };
    return true;
  }

  public static boolean regexpMatches(String pat, String s) {
    return pat != null && s != null && regexp(pat, s).matches();
  }

  public static GazelleLine dm_discord_nextToLastEvalByUser(long userID) {
    List<GazelleLine> lines = lazyMap("dm_discord_importLine", dm_discord_linesByUser_unimported(userID));
    return nextToLastThat("gazelle_isEvalLine_withoutAgain", lines);
  }

  public static String replaceSuffix(String a, String b, String s) {
    return endsWith(s, a) ? dropLast(s, l(a)) + b : s;
  }

  public static String afterSpace(String s) {
    return dropUntilSpace(s);
  }

  public static F1<String, String> botEval_strictSafetyCheck() {
    return new F1<String, String>() {

      public String get(String code) {
        try {
          String safety = joinWithComma(getCodeFragmentSafety(code));
          if (eq(safety, "safe"))
            return "";
          String s = "Sorry. Safety level is " + safety;
          Set<String> unknown = codeAnalysis_getUnknownIdentifiers(code);
          if (nempty(unknown))
            s += ". Unknown identifiers: " + joinWithComma(unknown);
          return s;
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "S safety = joinWithComma(getCodeFragmentSafety(code));\r\n    if (eq(safety, 's...";
      }
    };
  }

  public static boolean dm_bot_execEvalCmd(VF1<String> postMessage, String s, Object... __) {
    double timeout = optPar("timeout", __, 60.0);
    Object safetyCheck = optPar("safetyCheck", __);
    Matches m = new Matches();
    boolean freshMe = swic_trim(s, "!fresh ", m);
    if (freshMe || eq(s, "!fresh")) {
      dm_refreshTranspiler();
      if (!freshMe) {
        callF(postMessage, "OK");
        return true;
      }
    }
    if (swic_trim(s, "!eval ", m)) {
      callF(postMessage, pcallOrExceptionText_strOrNull(new F0<Object>() {

        public Object get() {
          try {
            String code = m.rest();
            {
              String _a_44 = (String) callF(safetyCheck, code);
              if (!empty(_a_44))
                return _a_44;
            }
            return strOrNull(dm_javaEvalOrInterpret(code));
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "S code = m.rest();\r\n      try answer (S) callF(safetyCheck, code);\r\n      ret...";
        }
      }));
      return true;
    }
    if (freshMe || swic_trim(s, "!real-eval ", m)) {
      callF(postMessage, evalWithTimeout_text(timeout, new F0<Object>() {

        public Object get() {
          try {
            String code = m.rest();
            {
              String _a_45 = (String) callF(safetyCheck, code);
              if (!empty(_a_45))
                return _a_45;
            }
            return dm_javaEval(code);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "S code = m.rest();\r\n      try answer (S) callF(safetyCheck, code);\r\n      ret...";
        }
      }));
      return true;
    }
    return false;
  }

  public static int shorten_default = 100;

  public static String shorten(String s) {
    return shorten(s, shorten_default);
  }

  public static String shorten(String s, int max) {
    return shorten(s, max, "...");
  }

  public static String shorten(String s, int max, String shortener) {
    if (s == null)
      return "";
    if (max < 0)
      return s;
    return s.length() <= max ? s : substring(s, 0, min(s.length(), max - l(shortener))) + shortener;
  }

  public static String shorten(int max, String s) {
    return shorten(s, max);
  }

  public static String ifEmpty(String s, String b) {
    return empty(s) ? b : s;
  }

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

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

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

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

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

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

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

  public static <A> A nextToLast(List<A> l) {
    return get(l, l(l) - 2);
  }

  public static <A> A nextToNextToLast(List<A> l) {
    return get(l, l(l) - 3);
  }

  public static String leadingSquareBracketStuff(String s) {
    if (!startsWith(s, '['))
      return null;
    return substring(s, 1, smartIndexOf(s, 1, ']'));
  }

  public static String dropActuallyLeadingSquareBracketStuff(String s) {
    return startsWith(s, "[") ? dropLeadingSquareBracketStuff(s) : s;
  }

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

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

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

  public static 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;
  }

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

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

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

  public static boolean cic(Collection<String> l, String s) {
    return containsIgnoreCase(l, s);
  }

  public static boolean cic(String[] l, String s) {
    return containsIgnoreCase(l, s);
  }

  public static boolean cic(String s, char c) {
    return containsIgnoreCase(s, c);
  }

  public static boolean cic(String a, String b) {
    return containsIgnoreCase(a, b);
  }

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

  public static Pair<String, String> tok_splitAtDoubleArrow_pair(String s) {
    return splitAtDoubleArrow_pair(s);
  }

  public static Pair<String, String> tok_splitAtDoubleArrow_pair(List<String> tok) {
    return splitAtDoubleArrow_pair(tok);
  }

  public static String optCurly(String s) {
    return curlyBraceOptIfMoreThanOneToken(s);
  }

  public static void gazelle_parsePublicRuleOptions(String opt, List<String> comments) {
    Matches m = new Matches();
    for (String s : tok_splitAtComma(opt)) {
      if (match("with *", s, m))
        if (remoteMechListMirror_isPublic(m.unq(0)))
          comments.add("use helper table mech list " + quote(m.unq(0)));
      if (matchStartX("purpose: ...", s, m))
        comments.add(s);
    }
  }

  public static <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;
  }

  public static String replace(String s, String a, String b) {
    return s == null ? null : a == null || b == null ? s : s.replace(a, b);
  }

  public static String replace(String s, char a, char b) {
    return s == null ? null : s.replace(a, b);
  }

  public static String jreplace1(String s, String in, String out) {
    return jreplace1(s, in, out, null);
  }

  public static String jreplace1(String s, String in, String out, Object condition) {
    List<String> tok = javaTok(s);
    return jreplace1(tok, in, out, condition) ? join(tok) : s;
  }

  public static boolean jreplace1(List<String> tok, String in, String out) {
    return jreplace1(tok, in, out, false, true, null);
  }

  public static boolean jreplace1(List<String> tok, String in, String out, Object condition) {
    return jreplace1(tok, in, out, false, true, condition);
  }

  public static boolean jreplace1(List<String> tok, String in, String out, boolean ignoreCase, boolean reTok, Object condition) {
    List<String> tokin = javaTok(in);
    jfind_preprocess(tokin);
    boolean anyChange = false;
    int i = 1;
    while ((i = findCodeTokens(tok, i, ignoreCase, toStringArray(codeTokensOnly(tokin)), condition)) >= 0) {
      List<String> subList = tok.subList(i - 1, i + l(tokin) - 1);
      String expansion = jreplaceExpandRefs(out, subList);
      int end = i + l(tokin) - 2;
      clearAllTokens(tok, i, end);
      tok.set(i, expansion);
      if (reTok)
        reTok(tok, i, end);
      i = end;
      anyChange = true;
    }
    return anyChange;
  }

  public static boolean jreplace1_debug;

  public static String gazelle_processSquareBracketAnnotations(String ruleText, List<String> comments) {
    List<String> lines = tlft(ruleText);
    while (isSquareBracketed(last(lines))) comments.add(deSquareBracket(popLast(lines)));
    int out = -1;
    for (int i = 0; i < l(lines); i++) {
      String s = lines.get(i);
      Pair<String, String> p = splitTrailingSquareBracketStuff(s);
      if (startsWith(s, "=>"))
        out = i;
      if (nempty(p.b))
        comments.add((out >= 0 ? i == out && i == l(lines) - 1 ? "out" : "out " + (i - out + 1) : "in " + (i + 1)) + " = " + gazelle_canonicalizeLineType(p.b));
      lines.set(i, p.a);
    }
    return lines_rtrim(lines);
  }

  public static ThreadLocal<String> dm_gazelle_addRuleWithComment_msg = new ThreadLocal();

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

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

  public static Pair<String, Boolean> dm_gazelle_addRuleWithComment(String text, String comment) {
    Pair<String, Boolean> p = (Pair<String, Boolean>) (quickImport(dm_call(dm_gazelle_rulesModule(), "addRuleWithComment", text, comment)));
    String rendered = newLinesToSpaces_trim(text);
    if (nempty(comment) && !isTrue(dm_gazelle_addRuleWithComment_renderWithoutComments))
      rendered += " [comments: " + joinLinesWithComma(comment) + "]";
    String _msg = setTLAndReturn(dm_gazelle_addRuleWithComment_msg, p.b ? "Rule " + p.a + " added: " + rendered : "Rule already there: " + p.a + " - " + rendered);
    if (!boolPar(dm_gazelle_addRuleWithComment_noInfoBox))
      infoBox(_msg);
    return p;
  }

  public static Pair<String, Boolean> dm_gazelle_addRuleWithComment(String when, String then, String comment) {
    return dm_gazelle_addRuleWithComment(when + "\n=> " + then, comment);
  }

  public static String lines_rtrim(Collection lines) {
    return rtrim_fromLines(lines);
  }

  public static Pair<String, Boolean> gazelle_createRuleForPhrase(String phrase) {
    return dm_gazelle_addRuleWithComment(phrase + " => phrase", "purpose: phrase");
  }

  public static boolean swic(String a, String b) {
    return startsWithIgnoreCase(a, b);
  }

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

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

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

  public static String substring(String 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.substring(x, y);
  }

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

  public static String trimSubstring(String s, int x, int y) {
    return trim(substring(s, x, y));
  }

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

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

  public static String takeLast(int n, String s) {
    return substring(s, l(s) - n);
  }

  public static String takeLast(String s, int n) {
    return substring(s, l(s) - n);
  }

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

  public static <A> List<A> dropLast(List<A> l) {
    return subList(l, 0, l(l) - 1);
  }

  public static <A> List<A> dropLast(int n, List<A> l) {
    return subList(l, 0, l(l) - n);
  }

  public static <A> List<A> dropLast(Iterable<A> l) {
    return dropLast(asList(l));
  }

  public static String dropLast(String s) {
    return substring(s, 0, l(s) - 1);
  }

  public static String dropLast(String s, int n) {
    return substring(s, 0, l(s) - n);
  }

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

  public static List collect(Collection c, String field) {
    return collectField(c, field);
  }

  public static List collect(String field, Collection c) {
    return collectField(c, field);
  }

  public static GazelleContextCache_v2 gazelle_cloneContextCache(GazelleContextCache_v2 cc) {
    GazelleContextCache_v2 cc2 = new GazelleContextCache_v2();
    cc2.cachedCtx = gazelle_cloneCtx(cc.cachedCtx);
    cc2.debug = cc.debug;
    return cc2;
  }

  public static Object[] litmapparams(Object... l) {
    return new Object[] { litmap(l) };
  }

  public static MechListDependentCaches<TreeSet<String>> mechCISet_cache = new MechListDependentCaches(new F1<String, TreeSet<String>>() {

    public TreeSet<String> get(String text) {
      try {
        return asCISet(tlft(text));
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public String toString() {
      return "asCISet(tlft(text))";
    }
  });

  public static TreeSet<String> mechCISet(String name) {
    return mechCISet_cache.get(name);
  }

  public static List<GazelleTree> gazelle_wrapLinesAsTree(List<String> l) {
    return map(l, new F1<String, GazelleTree>() {

      public GazelleTree get(String s) {
        try {
          return new GazelleTree(s);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "GazelleTree(s)";
      }
    });
  }

  public static List<String> gazelle_answersFromModules(String userID, String input) {
    List<String> out = new ArrayList();
    List<String> modules = (List<String>) (dm_call(dm_gazelle_modulesManager(), "activeModulesForUser", userID));
    for (String moduleID : unnull(modules)) {
      try {
        addIfNempty(out, (String) dm_callOpt(moduleID, "answer", input));
      } catch (Throwable __e) {
        _handleException(__e);
      }
    }
    return out;
  }

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

  public static <A> List<A> takeFirst(List<A> l, int n) {
    return l(l) <= n ? l : newSubListOrSame(l, 0, n);
  }

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

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

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

  public static <A> List<A> takeFirst(int n, Iterable<A> i) {
    List l = new ArrayList();
    Iterator<A> it = i.iterator();
    for (int _repeat_739 = 0; _repeat_739 < n; _repeat_739++) {
      if (it.hasNext())
        l.add(it.next());
      else
        break;
    }
    return l;
  }

  public static Collection<GInterpretable> gazelle_preprocess(String input1, Object... __) {
    List<GInterpretable> interpretables = new ArrayList();
    int max = optPar("max", __, 20);
    Collector<GInterpretable> collector = limitedCollector(interpretables, max);
    Map<String, String> evalCache = new HashMap();
    F1<String, String> eval = boolPar("noEvals", __) ? f_id() : new F1<String, String>() {

      public String get(String s) {
        try {
          return gazelle_processSafeEvals(s, evalCache);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "gazelle_processSafeEvals(s, evalCache)";
      }
    };
    input1 = callF(eval, input1);
    Set<String> texts = litCISet(input1);
    collector.add(new GInterpretable(input1));
    RuleEngine2 engine = ((F0<GazelleEvalContext>) optPar("contextMaker", __)).get().engine;
    int nInterpreted = 0;
    bigLoop: while (!collector.full() && nInterpreted < l(interpretables)) {
      GInterpretable workingOn = get(interpretables, nInterpreted);
      String input = workingOn.text;
      String ruleBase = empty(workingOn.ruleID) ? "" : workingOn.ruleID + "+";
      ++nInterpreted;
      for (RuleEngine2.SimplifyWithRule rule : engine.splitterRules()) {
        try {
          for (String out : gazelle_executeSplitterRule(engine, rule, input)) if (texts.add(out = callF(eval, out)))
            if (collector.add(new GInterpretable(out, ruleBase + rule.globalID)))
              break bigLoop;
        } catch (Throwable __e) {
          _handleException(__e);
        }
      }
      for (GazelleTree t : dm_gazelle_reasonAboutChatInput_v2(null, input, paramsPlus(__, "skipSplitters", true, "acceptablePurposes", litset("preprocess"), "skipUserName", true, "skipUserID", true))) {
        String s = callF(eval, t.line);
        if (texts.add(s))
          if (collector.add(new GInterpretable(s, ruleBase + t.ruleID(), t.mr)))
            break bigLoop;
      }
    }
    return interpretables;
  }

  public static List<GazelleTree> gazelle_postprocess(List<GazelleTree> l, Object... __) {
    l = takeFirst_clone(10, l);
    if (!boolPar("noEvals", __))
      gazelle_processSafeEvalsInTree(l);
    return l;
  }

  public static List<GazelleTree> dm_gazelle_reasonAboutChatInput_v2(String userName, String content, Object... __) {
    boolean debug = boolPar("debug", __);
    boolean debugRules = boolPar("debugRules", __) || debug;
    List<String> preContext = (List<String>) (optPar("preContext", __));
    GazelleEvalContext ctx = (GazelleEvalContext) (optPar("ctx", __));
    F0<GazelleEvalContext> contextMaker = (F0<GazelleEvalContext>) (optPar("contextMaker", __));
    if (debugRules)
      print("Have ctx: " + (ctx != null) + ", contextMaker: " + (contextMaker != null));
    if (ctx == null && contextMaker != null) {
      ctx = contextMaker.get();
      if (debugRules)
        print("Called contextMaker, got " + n2(ctx.engine.rules, "rule"));
    }
    if (ctx == null)
      ctx = dm_gazelle_stdEvalContext(dm_gazelle_allRulesWithComment(optPar("requiredComment", __, "discord")));
    Set<String> acceptablePurposes = (Set<String>) (optPar("acceptablePurposes", __));
    if (debug)
      print(" == PROCESSING INPUT: " + content);
    gazelle_dropRulesNotOnInput(ctx);
    if (acceptablePurposes != null) {
      if (debugRules) {
        print("Purposes: " + acceptablePurposes);
        print("Have purposes: " + collectUnique("purpose", ctx.engine.rules));
      }
      int nBefore = l(ctx.engine.rules);
      ctx.engine.dropRulesWhere(r -> !acceptablePurposes.contains(unnull(r.purpose)));
      if (debugRules)
        print("Retaining " + n2(ctx.engine.rules, "rule") + " of " + nBefore);
    }
    if (debugRules)
      print("Have rules: " + collect("globalID", ctx.engine.rules));
    gazelle_addHelpers(ctx, __);
    Object[] params = paramsPlus(paramsMinus(__, "ctx"), "firstConditionOnly", true, "dialogHistory", preContext);
    GazelleTree tree1 = (GazelleTree) (optPar("tree", __));
    if (tree1 == null)
      tree1 = new GazelleTree(ctx, content);
    List<GazelleTree> l = gazelle_getChildren_withContinuations(tree1, params);
    if (nempty(userName) && !boolPar("skipUserName", __)) {
      GazelleTree tree2 = new GazelleTree(ctx, optCurly(userName) + " says: " + content);
      if (debug)
        print("Tree2: " + tree2);
      l.addAll(gazelle_getChildren_withContinuations(tree2, paramsPlus(__, "tree2", true)));
    }
    Long userID = (Long) (optPar("userID", __));
    if (userID != null && userID != 0 && !boolPar("skipUserID", __)) {
      GazelleTree tree3 = new GazelleTree(ctx, "user with id " + userID + " says: " + content);
      l.addAll(gazelle_getChildren_withContinuations(tree3, paramsPlus(__, "tree3", true)));
    }
    if (boolPar("skipBad", __, true))
      l = filter(l, new F1<GazelleTree, Boolean>() {

        public Boolean get(GazelleTree t) {
          try {
            return neq(t.prediction, "bad");
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "neq(t.prediction, \"bad\")";
        }
      });
    gazelle_sortChildren(l);
    Map<String, String> inputMap = gazelle_inputMapFromLineAndHistory(content, listPlus(preContext, content));
    if (debug)
      print("reasonAbout inputMap=" + sfu(asTreeMap(inputMap)));
    for (GazelleTree t : l) {
      if (t.mr == null)
        continue;
      int iCond;
      do {
        iCond = t.mr.iCond;
        dm_gazelle_matchStep_statement(t, passParams(__, "debug", "dm_gazelle_matchStatementsDebug"));
        dm_gazelle_matchStep_additionalInput(t, inputMap, __);
        dm_gazelle_matchStep_condition(t, __);
      } while (t.mr.iCond > iCond);
    }
    for (GazelleTree t : l) t.flush();
    int n = l(l);
    l = gazelle_dropUnsatisfiedEntries(l);
    if (debug)
      print("Filtered out " + (n - l(l)) + " unsatisfied entries (keeping " + l(l) + ")");
    if (!boolPar("keepDefaults", __))
      gazelle_dropMatchedDefaultRules(l, __);
    return l;
  }

  public static List<GazelleTree> gazelle_reason_repeat(String input, Object... __) {
    List<GazelleTree> out = new ArrayList();
    int steps = 0, max = 10;
    while (steps++ < max) {
      addAll(out, (List<GazelleTree>) callF(optPar("sendToModules", __), input));
      List<GazelleTree> l = gazelle_reasonWithPreAndPost(input, paramsMinus(__, "sendToModules"));
      if (empty(l))
        break;
      gazelle_sortBeforeCommit(l, __);
      GazelleTree t = first(l);
      if (boolPar("debug", __) || boolPar("debugCommit", __))
        print("Committing to: " + t.line + " (out of " + l(l) + ")");
      List<String> lines = tok_splitAtPlusAtBeginningOfLine(t.line);
      if (l(lines) == 2 && t.rule() != null && contains(t.rule().comments, "out 2 = process")) {
        print("splitting!");
        t.line = first(lines);
        out.add(t);
        input = second(lines);
        continue;
      }
      out.add(t);
      break;
    }
    return out;
  }

  public static boolean dm_gazelle_hasTempFact(String text) {
    return (boolean) dm_requireAndCall("#1021809/GazelleTempFacts", "hasTempFact", text);
  }

  public static Pair<String, Boolean> dm_gazelle_addTempFact(String text, String context) {
    return (Pair<String, Boolean>) quickImport(dm_requireAndCall("#1021809/GazelleTempFacts", "addTempFact", text, context));
  }

  public static Set<String> mechSet(String name) {
    return asHashSet(mechList(name));
  }

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

  public static Object dm_javaEvalOrInterpret(String code) {
    final Object module = dm_current_generic();
    code = trim(code);
    if (canQuickEval(code))
      return quickEval(code);
    Pair<String, List<String>> p = tok_parseFunctionCall(code);
    if (p != null) {
      if (module != null)
        makeAndCall_initChild.set(new F1<Class, AutoCloseable>() {

          public AutoCloseable get(Class c) {
            try {
              shareMyCreatorWith(c);
              InheritableThreadLocal<WeakReference> tl = (InheritableThreadLocal<WeakReference>) (getOpt(c, "dm_currentModule_generic"));
              return tempSetTL(tl, weakRef(module));
            } catch (Exception __e) {
              throw rethrow(__e);
            }
          }

          public String toString() {
            return "shareMyCreatorWith(c);\r\n      InheritableThreadLocal<WeakReference> tl = cast...";
          }
        });
      return callAndMake(p.a, toObjectArray(map("dm_javaEvalOrInterpret", p.b)));
    }
    return dm_javaEval(code);
  }

  public static <A> A nu(Class<A> c, Object... values) {
    A a = nuObject(c);
    setAll(a, values);
    return a;
  }

  public static void dm_gazelle_saveReasoningForLine(Gazelle_ReasoningForLine reasoning) {
    saveGZStructToFileVerbose(gazelle_discord_reasoningFile(reasoning), reasoning);
    if (nempty(reasoning.outContext))
      gazelle_copyLineToLongAppliedRuleList(reasoning.outContext);
    else
      dm_discord_copyLineToLongAppliedRuleList(reasoning.outMsgID);
  }

  public static void gazelle_storeRecentlyUsedMappings(List<GazelleTree> l, Object... __) {
    for (GazelleTree t : unnull(l)) {
      try {
        if (t.rule() != null && t.varMap() != null && t.rule().avoidRecentlyUsedMappings)
          dm_call(gazelle_rumCRUD(), "storeRecentlyUsedMapping", or2(stringPar("context", __), "unknown"), t.rule().globalID, t.varMap());
      } catch (Throwable __e) {
        _handleException(__e);
      }
    }
  }

  public static String tok_dropCurlyBrackets(String s) {
    return join(tok_dropCurlyBrackets(javaTok(s)));
  }

  public static List<String> tok_dropCurlyBrackets(List<String> tok) {
    for (int i = 1; i < l(tok); i += 2) {
      while (eqGetOneOf(tok, i, "{", "}")) {
        tokAppend(tok, i - 1, tok.get(i + 1));
        removeSubList(tok, i, i + 2);
      }
    }
    return tok;
  }

  public static String exceptionToStringShort(Throwable e) {
    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;
  }

  public static String getChannel() {
    return getChannelName();
  }

  public static String gazelle_postedLinesCRUD() {
    return dm_require("#1022603/GPostedLines");
  }

  public static <A> A popFirst(List<A> l) {
    if (empty(l))
      return null;
    A a = first(l);
    l.remove(0);
    return a;
  }

  public static <A> A popFirst(Collection<A> l) {
    if (empty(l))
      return null;
    A a = first(l);
    l.remove(a);
    return a;
  }

  public static long dm_discord_channelID(String name) {
    return (long) dm_call(dm_gazelle_linesCRUD(), "getChannelID", name);
  }

  public static Object dm_pcall(Object moduleOrID, String method, Object... args) {
    try {
      return dm_call(moduleOrID, method, args);
    } catch (Throwable __e) {
      return null;
    }
  }

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

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

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

  public static 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 timer;
  }

  public static java.util.Timer doLater(double delaySeconds, final Object r) {
    return doLater(toMS(delaySeconds), r);
  }

  public static String absoluteURL(String url) {
    return isAbsoluteURL(url) ? url : "http://" + url;
  }

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

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

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

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

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

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

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

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

  public static List<GazelleLine> dm_discord_linesInChannelBy(long channelID, long userID) {
    return map("dm_discord_importLine", (List) dm_call(dm_gazelle_linesCRUD(), "linesInChannelBy", channelID, userID));
  }

  public static List<GazelleLine> dm_discord_linesInChannelBy(String channelName, String userName) {
    return dm_discord_linesInChannelBy((long) dm_call(dm_gazelle_linesCRUD(), "idForChannel", channelName), (long) dm_call(dm_gazelle_linesCRUD(), "idForUser", userName));
  }

  public static List<GazelleTree> gazelle_reasonAbout(String line, Object... __) {
    return dm_gazelle_reasonAbout(line, __);
  }

  public static Object[] paramsPlus(Object[] a1, Object... a2) {
    if (a2 == null)
      return a1;
    if (a1 == null)
      return a2;
    if (l(a1) == 1 && a1[0] instanceof Map)
      return new Object[] { mapPlus((Map) a1[0], a2) };
    assertEvenLength(a1);
    assertEvenLength(a2);
    Map map = paramsToOrderedMap(a1);
    int n = l(a2);
    for (int i = 0; i < n; i += 2) {
      Object key = a2[i];
      if (key != null)
        map.put(key, a2[i + 1]);
    }
    return mapToParams(map);
  }

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

  public static String str(char[] c) {
    return new String(c);
  }

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

  public static Object call_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.findStaticMethod(method, args);
        if (me != null)
          return invokeMethod(me, null, args);
        List<Method> methods = cache.cache.get(method);
        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() + "." + method + "(" + joinWithComma(classNames(args)) + ") not found");
      } 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);
          }
        throw fail("Method " + c.getName() + "." + method + "(" + joinWithComma(classNames(args)) + ") not found");
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

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

  public static void setDefaultClassFinder(Object cf) {
    _defaultClassFinder_value = cf;
  }

  public static HashMap<String, Class> findClass_fullName_cache = new HashMap();

  public static 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;
    }
  }

  public static Throwable printStackTrace2(Throwable e) {
    print(getStackTrace2(e));
    return e;
  }

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

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

  public static Object _defaultClassFinder_value;

  public static Object _defaultClassFinder() {
    return _defaultClassFinder_value;
  }

  public static String programID;

  public static String getProgramID() {
    return nempty(programID) ? formatSnippetIDOpt(programID) : "?";
  }

  public static String getProgramID(Class c) {
    String id = (String) getOpt(c, "programID");
    if (nempty(id))
      return formatSnippetID(id);
    return "?";
  }

  public static String getProgramID(Object o) {
    return getProgramID(getMainClass(o));
  }

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

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

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

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

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

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

  public static 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();
  }

  public static void print_append(Appendable _buf, String s, int max) {
    try {
      synchronized (_buf) {
        _buf.append(s);
        if (!(_buf instanceof StringBuilder))
          return;
        StringBuilder buf = (StringBuilder) _buf;
        max /= 2;
        if (buf.length() > max)
          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);
          }
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static java.awt.Color awtColor(String hex) {
    byte[] b = bytesFromHex(dropPrefix("#", hex));
    return new Color(ubyteToInt(b[0]), ubyteToInt(b[1]), ubyteToInt(b[2]));
  }

  public static TreeSet<String> caseInsensitiveSet() {
    return new TreeSet(caseInsensitiveComparator());
  }

  public static TreeSet<String> caseInsensitiveSet(Collection<String> c) {
    return toCaseInsensitiveSet(c);
  }

  public static <A> TreeSet<A> treeSet() {
    return new TreeSet();
  }

  public static String vm_cleanForPrint(Object o) {
    String s = str(o);
    return or((String) vmBus_query("cleanForPrint", s), s);
  }

  public static File getProgramFile(String progID, String fileName) {
    if (new File(fileName).isAbsolute())
      return new File(fileName);
    return new File(getProgramDir(progID), fileName);
  }

  public static File getProgramFile(String fileName) {
    return getProgramFile(getProgramID(), fileName);
  }

  public static void appendToTextFile(File file, String contents) {
    try {
      File parentFile = file.getParentFile();
      if (parentFile != null)
        parentFile.mkdirs();
      if (contents != null) {
        FileOutputStream fileOutputStream = new FileOutputStream(file, true);
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8");
        PrintWriter printWriter = new PrintWriter(outputStreamWriter);
        printWriter.print(contents);
        printWriter.close();
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static void appendToTextFile(String file, String contents) {
    appendToTextFile(programFile(file), contents);
  }

  public static String localDateWithMilliseconds(long time) {
    SimpleDateFormat format = simpleDateFormat_local("yyyy/MM/dd HH:mm:ss''SSSS");
    return format.format(time);
  }

  public static String localDateWithMilliseconds() {
    return localDateWithMilliseconds(now());
  }

  public static void print_forAllThreads(Object f) {
    assertNull("todo", print_allThreads);
    print_allThreads = f;
  }

  public static void dm_useLocallyCopiedMechLists() {
    dm_requireModule("#1016073/MechLists");
    useLocalMechListCopies(true);
    setOptMC("serverMechList_raw_fresh_verbose", true);
    setOptMC("mechList_goToCreator_dont", true);
  }

  public static volatile boolean ping_pauseAll;

  public static int ping_sleep = 100;

  public static volatile boolean ping_anyActions;

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

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

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

  public static 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);
    }
  }

  public static Set<AutoCloseable> _registerAutoCloseable_set = synchroHashSet();

  public static void _registerAutoCloseable(AutoCloseable c) {
    addIfNotNull(_registerAutoCloseable_set, c);
  }

  public static void cleanMeUp__registerAutoCloseable() {
    closeAutoCloseables(getAndClearList(_registerAutoCloseable_set));
  }

  public static File javaxSecretDir_dir;

  public static File javaxSecretDir() {
    return javaxSecretDir_dir != null ? javaxSecretDir_dir : new File(userHome(), "JavaX-Secret");
  }

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

  public static <A> A assertNempty(A a) {
    return assertNempty("empty", a);
  }

  public static <A> A assertNempty(String msg, A a) {
    if (empty(a))
      throw fail(msg + ": " + a);
    return a;
  }

  public static String loadTextFile(String fileName) {
    return loadTextFile(fileName, null);
  }

  public static String loadTextFile(File f, String defaultContents) {
    try {
      checkFileNotTooBigToRead(f);
      if (f == null || !f.exists())
        return defaultContents;
      FileInputStream fileInputStream = new FileInputStream(f);
      InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8");
      return loadTextFile(inputStreamReader);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static String loadTextFile(File fileName) {
    return loadTextFile(fileName, null);
  }

  public static String loadTextFile(String fileName, String defaultContents) {
    return fileName == null ? defaultContents : loadTextFile(newFile(fileName), defaultContents);
  }

  public static 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);
  }

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

  public static <A> List<A> synchroList(List<A> l) {
    return Collections.synchronizedList(l);
  }

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

  public static Throwable innerException(Throwable e) {
    return getInnerException(e);
  }

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

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

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

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

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

  public static String dm_require(String moduleLibID) {
    return dm_makeModule(moduleLibID);
  }

  public static Object dm_getModule(Object moduleOrID) {
    if (moduleOrID == null || eq(moduleOrID, ""))
      return null;
    if (isString(moduleOrID) && isIdentifier(((String) moduleOrID)))
      return dm_getService(((String) moduleOrID));
    if (isStringOrIntOrLong(moduleOrID))
      return dm_callOS("getDynModuleByID", str(moduleOrID));
    return dm_resolveModule(moduleOrID);
  }

  public static AutoCloseable dm_enter(Object mod) {
    return (AutoCloseable) callOpt(dm_getModule(mod), "enter");
  }

  public static Object[] litObjectArray(Object... l) {
    return l;
  }

  public static Object litobjarrayAsObject(Object... l) {
    return l;
  }

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

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

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

  public static Set vm_busListeners_live_cache;

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

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

  public static Map<String, Set> vm_busListenersByMessage_live_cache;

  public static 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;
  }

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

  public static void addToContainer(final Container a, final Component b) {
    if (a != null && b != null) {
      swing(new Runnable() {

        public void run() {
          try {
            a.add(b);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "a.add(b);";
        }
      });
    }
  }

  public static <A> List<A> concatLists(Collection<A>... lists) {
    List<A> l = new ArrayList();
    if (lists != null)
      for (Collection<A> list : lists) if (list != null)
        l.addAll(list);
    return l;
  }

  public static <A> List<A> concatLists(Collection<? extends Collection<A>> lists) {
    List<A> l = new ArrayList();
    if (lists != null)
      for (Collection<A> list : lists) if (list != null)
        l.addAll(list);
    return l;
  }

  public static String gazelle_discord_matchedRuleStructForMsgID(long msgID) {
    return gazelle_matchedRuleStructFromReasoningForLine(unstructGZFile(gazelle_discord_reasoningFileForMsgID(msgID)));
  }

  public static <A> A printIndent(A o) {
    print(indentx(str(o)));
    return o;
  }

  public static <A> A printIndent(String indent, A o) {
    print(indentx(indent, str(o)));
    return o;
  }

  public static void printIndent(int indent, Object o) {
    print(indentx(indent, str(o)));
  }

  public static List<String> uniquifyCI(Collection<String> l) {
    TreeSet<String> set = ciSet();
    List<String> out = new ArrayList();
    for (String a : unnull(l)) if (set.add(a))
      out.add(a);
    return out;
  }

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

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

  public static <A, B> List<B> map(Iterable<A> l, F1<A, B> f) {
    return map(f, l);
  }

  public static <A, B> List<B> map(F1<A, B> f, Iterable<A> l) {
    List x = emptyList(l);
    if (l != null)
      for (A o : l) x.add(callF(f, o));
    return x;
  }

  public static <A, B> List<B> map(Iterable<A> l, IF1<A, B> f) {
    List x = emptyList(l);
    if (l != null)
      for (A o : l) x.add(f.get(o));
    return x;
  }

  public static <A, B> List<B> map(A[] l, IF1<A, B> f) {
    List x = emptyList(l);
    if (l != null)
      for (A o : l) x.add(f.get(o));
    return x;
  }

  public static List map(Object f, Object[] l) {
    return map(f, asList(l));
  }

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

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

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

  public static String dm_posNegStringForEmoji(String unicode) {
    return (String) dm_requireAndCall("#1021646/EmojisCRUD", "getPosNeg", unicode);
  }

  public static boolean warn_on = true;

  public static void warn(String s) {
    if (warn_on)
      print("Warning: " + s);
  }

  public static void warn(String s, List<String> warnings) {
    warn(s);
    if (warnings != null)
      warnings.add(s);
  }

  public static void dm_gazelle_saveAppliedRule(Object... params) {
    dm_requireAndCall("#1021413/AppliedRules", "uniqConcept", (Object) params);
  }

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

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

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

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

  public static Android3 bot(String greeting) {
    return makeAndroid3(greeting);
  }

  public static Android3 bot(Android3 a) {
    return makeBot(a);
  }

  public static Android3 bot(String greeting, Object responder) {
    return makeBot(greeting, responder);
  }

  public static Android3 bot() {
    return makeBot();
  }

  public static long getLong(Object o, String field) {
    return toLong(getOpt(o, field));
  }

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

  public static List getList(Map map, Object key) {
    return map == null ? null : (List) map.get(key);
  }

  public static List getList(List l, int idx) {
    return (List) get(l, idx);
  }

  public static List getList(Object o, Object key) {
    if (o instanceof Map)
      return getList((Map) o, key);
    if (key instanceof String)
      return (List) getOpt(o, (String) key);
    throw fail("Not a string key: " + getClassName(key));
  }

  public static String dm_moduleID(Object module) {
    return strOrNull(getOpt(dm_getStem(module), "id"));
  }

  public static String dm_moduleID() {
    return dm_moduleID(dm_current_mandatory());
  }

  public static AutoCloseable combineAutoCloseables(final AutoCloseable a, final AutoCloseable b) {
    return a == null ? b : b == null ? a : new AutoCloseable() {

      public String toString() {
        return "a.close(); b.close();";
      }

      public void close() throws Exception {
        a.close();
        b.close();
      }
    };
  }

  public static AutoCloseable combineAutoCloseables(AutoCloseable a, AutoCloseable b, AutoCloseable c, AutoCloseable... more) {
    return combineAutoCloseables(concatLists(ll(a, b, c), asList(more)));
  }

  public static AutoCloseable combineAutoCloseables(Iterable<AutoCloseable> l) {
    return foldl(new F2<AutoCloseable, AutoCloseable, AutoCloseable>() {

      public AutoCloseable get(AutoCloseable a, AutoCloseable b) {
        try {
          return combineAutoCloseables(a, b);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "combineAutoCloseables(a,b)";
      }
    }, null, l);
  }

  public static AutoCloseable dm_vmBus_answerToMessage(String msg, final F0 f) {
    final DynModule m = dm_current_mandatory();
    return dm_ownResource(vmBus_addListener_basic(msg, new F2<String, Object, Object>() {

      public Object get(String _msg, Object arg) {
        try {
          AutoCloseable __12 = m.enter();
          try {
            return callF(f);
          } finally {
            _close(__12);
          }
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "temp m.enter();\r\n    ret callF(f);";
      }
    }));
  }

  public static AutoCloseable dm_vmBus_answerToMessage(String msg, final F1 f) {
    final DynModule m = dm_current_mandatory();
    return dm_ownResource(vmBus_addListener_basic(msg, new F2<String, Object, Object>() {

      public Object get(String _msg, Object arg) {
        try {
          AutoCloseable __13 = m.enter();
          try {
            return callF(f, arg);
          } finally {
            _close(__13);
          }
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "temp m.enter();\r\n    ret callF(f, arg);";
      }
    }));
  }

  public static <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);
      }
    };
  }

  public static <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;
  }

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

  public static <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).booleanValue())
        x.add(o);
    return x;
  }

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

  public static <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).booleanValue())
        x.add(o);
    return x;
  }

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

  public static List<T3<String, String, String>> gazelle_allRuleTriples() {
    return dm_allRulesFromRulesModuleWithCommentsAndIDs();
  }

  public static String gazelle_purposeFromComments(List<String> comments) {
    Matches m = new Matches();
    for (String s : unnull(comments)) {
      if (swic_trim(s, "purpose:", m))
        return m.rest();
    }
    return "";
  }

  public static String gazelle_purposeFromComments(String comments) {
    return gazelle_purposeFromComments(tlft(comments));
  }

  public static List<T3<String, String, String>> dm_gazelle_allRulesWithComment(final String comment) {
    List<T3<String, String, String>> l = dm_allRulesFromRulesModuleWithCommentsAndIDs();
    return empty(comment) ? l : filter(l, new F1<T3<String, String, String>, Boolean>() {

      public Boolean get(T3<String, String, String> t) {
        try {
          return cic(tlft(t.b), comment);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "cic(tlft(t.b), comment)";
      }
    });
  }

  public static List<String> javaTokWithBrackets(String s) {
    return javaTokPlusBrackets(s);
  }

  public static <A> Pair<A, A> listToPair(List<A> l) {
    return l(l) != 2 ? null : pair(first(l), second(l));
  }

  public static List<String> splitAtDoubleArrow(String s) {
    return splitAtDoubleArrow(javaTok(s));
  }

  public static List<String> splitAtDoubleArrow(List<String> tok) {
    List<String> l = new ArrayList();
    int i = 0;
    while (i < l(tok)) {
      int j = indexOfSubList(tok, ll("=", "", ">"), i);
      if (i >= l(tok))
        break;
      if (j < 0)
        j = l(tok);
      l.add(trimJoin(tok.subList(i, j)));
      i = j + 3;
    }
    return l;
  }

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

  public static boolean matchX_vbar(String pat, String s) {
    return matchX_vbar(pat, s, null);
  }

  public static boolean matchX_vbar(String pat, String s, Matches matches) {
    for (String pat2 : tok_splitAtVerticalBar(pat)) if (matchX(pat2, s, matches))
      return true;
    return false;
  }

  public static Object dm_requireAndCall(String moduleLibID, String method, Object... args) {
    return dm_requireAndCallModule(moduleLibID, method, args);
  }

  public static Matcher regexp(String pat, String s) {
    return regexp(compileRegexp(pat), unnull(s));
  }

  public static Matcher regexp(Pattern pat, String s) {
    return pat.matcher(unnull(s));
  }

  public static List lazyMap(final Object f, final List l) {
    return lazilyMap(f, l);
  }

  public static List dm_discord_linesByUser_unimported(long userID) {
    return (List) dm_call(dm_gazelle_linesCRUD(), "linesBy", userID);
  }

  public static <A> A nextToLastThat(List<A> l, Object pred) {
    int found = 0;
    for (int i = l(l) - 1; i >= 0; i--) {
      A a = l.get(i);
      if (checkCondition(pred, a))
        if (++found >= 2)
          return a;
    }
    return null;
  }

  public static <A> A nextToLastThat(Object pred, List<A> l) {
    return nextToLastThat(l, pred);
  }

  public static boolean gazelle_isEvalLine_withoutAgain(GazelleLine line) {
    return gazelle_isEvalLine(line) && !endsWith(line.text, "again");
  }

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

  public static boolean endsWith(String a, char c) {
    return nempty(a) && lastChar(a) == c;
  }

  public static boolean endsWith(String a, String b, Matches m) {
    if (!endsWith(a, b))
      return false;
    m.m = new String[] { dropLast(l(b), a) };
    return true;
  }

  public static String dropUntilSpace(String s) {
    if (s == null)
      return "";
    int i = s.indexOf(' ') + 1;
    while (i < l(s) && s.charAt(i) == ' ') ++i;
    return s.substring(i);
  }

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

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

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

  public static Collection<String> getCodeFragmentSafety(String code) {
    Map<String, String> map = codeAnalysis_identifierSafetyMap();
    Set<String> identifiers = tok_allIdentifiers(code);
    Collection<String> tags = treeSet();
    for (String id : identifiers) {
      String tag;
      if (codeAnalysis_isSafeIdentifier(id))
        tag = "safe";
      else
        tag = or2(map.get(id), "?");
      tags.addAll(tokSplitAtComma(tag));
    }
    tags = simplifySafetyTags(tags);
    if (empty(tags))
      tags.add("safe");
    return tags;
  }

  public static Set<String> codeAnalysis_getUnknownIdentifiers(String code) {
    Map<String, String> map = codeAnalysis_identifierSafetyMap();
    Set<String> identifiers = tok_allIdentifiers(code);
    TreeSet<String> set = new TreeSet();
    for (String id : identifiers) {
      if (!codeAnalysis_isSafeIdentifier(id) && (!map.containsKey(id) || contains(map.get(id), "?")))
        set.add(id);
    }
    return set;
  }

  public static <A> A optPar(ThreadLocal<A> tl, A defaultValue) {
    A a = tl.get();
    if (a != null) {
      tl.set(null);
      return a;
    }
    return defaultValue;
  }

  public static <A> A optPar(ThreadLocal<A> tl) {
    return optPar(tl, null);
  }

  public static Object optPar(Object[] params, String name) {
    return optParam(params, name);
  }

  public static Object optPar(String name, Object[] params) {
    return optParam(params, name);
  }

  public static <A> A optPar(Object[] params, String name, A defaultValue) {
    return optParam(params, name, defaultValue);
  }

  public static <A> A optPar(String name, Object[] params, A defaultValue) {
    return optParam(params, name, defaultValue);
  }

  public static void dm_refreshTranspiler() {
    call(dm_moduleOfType("#1017065/Transpiler"), "refresh");
  }

  public static String pcallOrExceptionText_strOrNull(Object f, Object... args) {
    try {
      return strOrNull(callF(f, args));
    } catch (Throwable e) {
      _handleException(e);
      return exceptionToStringShort(e);
    }
  }

  public static String evalWithTimeout_text(double timeoutSeconds, final Object f) {
    return evalWithTimeout_text(toMS_int(timeoutSeconds), f);
  }

  public static String evalWithTimeout_text(final int timeoutMS, final Object f) {
    return pcallOrExceptionText_strOrNull(new F0<Object>() {

      public Object get() {
        try {
          return evalWithTimeoutOrException(timeoutMS, new F0<Object>() {

            public Object get() {
              try {
                return strOrNull(callF(f));
              } catch (Exception __e) {
                throw rethrow(__e);
              }
            }

            public String toString() {
              return "ret strOrNull(callF(f));";
            }
          });
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "ret evalWithTimeoutOrException(timeoutMS, func -> O { strOrNull(callF(f)) });";
      }
    });
  }

  public static Object dm_javaEval(String expression) {
    return dm_evalJava_withModule(expression);
  }

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

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

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

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

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

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

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

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

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

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

  public static void _handleError(Error e) {
    call(javax(), "_handleError", e);
  }

  public static 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);
  }

  public static int smartIndexOf(String s, int i, char c) {
    return smartIndexOf(s, c, i);
  }

  public static 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);
  }

  public static int smartIndexOf(String s, String sub) {
    return smartIndexOf(s, sub, 0);
  }

  public static int smartIndexOf(String s, char c) {
    return smartIndexOf(s, c, 0);
  }

  public static <A> int smartIndexOf(List<A> l, A sub) {
    return smartIndexOf(l, sub, 0);
  }

  public static <A> int smartIndexOf(List<A> l, int start, A sub) {
    return smartIndexOf(l, sub, start);
  }

  public static <A> int smartIndexOf(List<A> l, A sub, int start) {
    int i = indexOf(l, sub, start);
    return i < 0 ? l(l) : i;
  }

  public static String dropLeadingSquareBracketStuff(String s) {
    return trimSubstring(s, indexOf(s, ']') + 1);
  }

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

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

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

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

  public static boolean containsIgnoreCase(Collection<String> l, String s) {
    if (l != null)
      for (String x : l) if (eqic(x, s))
        return true;
    return false;
  }

  public static boolean containsIgnoreCase(String[] l, String s) {
    if (l != null)
      for (String x : l) if (eqic(x, s))
        return true;
    return false;
  }

  public static boolean containsIgnoreCase(String s, char c) {
    return indexOfIgnoreCase(s, String.valueOf(c)) >= 0;
  }

  public static boolean containsIgnoreCase(String a, String b) {
    return indexOfIgnoreCase(a, b) >= 0;
  }

  public static String curlyBraceOptIfMoreThanOneToken(String s) {
    return !isCurlyBraced(s) && numberOfJavaTokens(s) > 1 ? curlyBrace(s) : s;
  }

  public static List<String> tok_splitAtComma(String s) {
    return tok_splitAtComma(javaTok(s));
  }

  public static List<String> tok_splitAtComma(List<String> tok) {
    List<String> out = new ArrayList();
    for (int i = 0; i < l(tok); i++) {
      int j = smartIndexOf(tok, ",", i);
      out.add(joinSubList(tok, i + 1, j - 1));
      i = j;
    }
    return out;
  }

  public static boolean remoteMechListMirror_isPublic(String listName) {
    return cic(remoteMechListMirrorStatus(listName), "PUBLIC READ");
  }

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

  public static 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();
  }

  public static 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 == '\0')
        out.append("\\0");
      else
        out.append(c);
    }
    out.append('"');
  }

  public static boolean matchStartX(String pat, String s, Matches matches) {
    return matchStart(dropSuffix("...", pat), s, matches);
  }

  public static int javaTok_n, javaTok_elements;

  public static boolean javaTok_opt;

  public static List<String> javaTok(String s) {
    ++javaTok_n;
    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 == '\'' && 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)) || "'".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));
      ++n;
      i = j;
    }
    if ((tok.size() % 2) == 0)
      tok.add("");
    javaTok_elements += tok.size();
    return tok;
  }

  public static List<String> javaTok(List<String> tok) {
    return javaTokWithExisting(join(tok), tok);
  }

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

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

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

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

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

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

  public static int jfind(String s, String in) {
    return jfind(javaTok(s), in);
  }

  public static int jfind(List<String> tok, String in) {
    return jfind(tok, 1, in);
  }

  public static int jfind(List<String> tok, int startIdx, String in) {
    return jfind(tok, startIdx, in, null);
  }

  public static int jfind(List<String> tok, String in, Object condition) {
    return jfind(tok, 1, in, condition);
  }

  public static int jfind(List<String> tok, int startIdx, String in, Object condition) {
    return jfind(tok, startIdx, javaTokForJFind_array(in), condition);
  }

  public static int jfind(List<String> tok, List<String> tokin) {
    return jfind(tok, 1, tokin);
  }

  public static int jfind(List<String> tok, int startIdx, List<String> tokin) {
    return jfind(tok, startIdx, tokin, null);
  }

  public static int jfind(List<String> tok, int startIdx, String[] tokinC, Object condition) {
    return findCodeTokens(tok, startIdx, false, tokinC, condition);
  }

  public static int jfind(List<String> tok, int startIdx, List<String> tokin, Object condition) {
    return findCodeTokens(tok, startIdx, false, codeTokensAsStringArray(tokin), condition);
  }

  public static 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;
  }

  public static int findCodeTokens(List<String> tok, String... tokens) {
    return findCodeTokens(tok, 1, false, tokens);
  }

  public static int findCodeTokens(List<String> tok, boolean ignoreCase, String... tokens) {
    return findCodeTokens(tok, 1, ignoreCase, tokens);
  }

  public static int findCodeTokens(List<String> tok, int startIdx, boolean ignoreCase, String... tokens) {
    return findCodeTokens(tok, startIdx, ignoreCase, tokens, null);
  }

  public static HashSet<String> findCodeTokens_specials = lithashset("*", "<quoted>", "<id>", "<int>", "\\*");

  public static boolean findCodeTokens_debug;

  public static int findCodeTokens_indexed, findCodeTokens_unindexed;

  public static int findCodeTokens_bails, findCodeTokens_nonbails;

  public static int findCodeTokens(List<String> tok, int startIdx, boolean ignoreCase, String[] tokens, Object condition) {
    if (findCodeTokens_debug) {
      if (eq(getClassName(tok), "main$IndexedList2"))
        findCodeTokens_indexed++;
      else
        findCodeTokens_unindexed++;
    }
    int end = tok.size() - tokens.length * 2 + 2, nTokens = tokens.length;
    int i = startIdx | 1;
    String firstToken = tokens[0];
    if (!ignoreCase && !findCodeTokens_specials.contains(firstToken)) {
      while (i < end && !firstToken.equals(tok.get(i))) i += 2;
    }
    outer: for (; i < end; i += 2) {
      for (int j = 0; j < nTokens; j++) {
        String p = tokens[j], t = tok.get(i + j * 2);
        boolean match;
        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;
  }

  public static 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;
  }

  public static 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));
  }

  public static List<String> codeTokensOnly(List<String> tok) {
    int n = tok.size();
    List<String> l = emptyList(n / 2);
    for (int i = 1; i < n; i += 2) l.add(tok.get(i));
    return l;
  }

  public static String jreplaceExpandRefs(String s, List<String> tokref) {
    List<String> tok = javaTok(s);
    for (int i = 1; i < l(tok); i += 2) {
      if (tok.get(i).startsWith("$") && isInteger(tok.get(i).substring(1))) {
        String x = tokref.get(-1 + parseInt(tok.get(i).substring(1)) * 2);
        tok.set(i, x);
      }
    }
    return join(tok);
  }

  public static void clearAllTokens(List<String> tok) {
    for (int i = 0; i < tok.size(); i++) tok.set(i, "");
  }

  public static void clearAllTokens(List<String> tok, int i, int j) {
    for (; i < j; i++) tok.set(i, "");
  }

  public static List<String> reTok(List<String> tok) {
    replaceCollection(tok, javaTok(tok));
    return tok;
  }

  public static List<String> reTok(List<String> tok, int i) {
    return reTok(tok, i, i + 1);
  }

  public static List<String> reTok(List<String> tok, int i, int j) {
    i = i & ~1;
    j = j | 1;
    List<String> t = javaTok(join(subList(tok, i, j)));
    replaceListPart(tok, i, j, t);
    return tok;
  }

  public static List<String> tlft(String s) {
    return toLinesFullTrim(s);
  }

  public static List<String> tlft(File f) {
    return toLinesFullTrim(f);
  }

  public static boolean isSquareBracketed(String s) {
    return s != null && s.startsWith("[") && s.endsWith("]");
  }

  public static <A> A last(List<A> l) {
    return empty(l) ? null : l.get(l.size() - 1);
  }

  public static char last(String s) {
    return empty(s) ? '#' : s.charAt(l(s) - 1);
  }

  public static int last(int[] a) {
    return l(a) != 0 ? a[l(a) - 1] : 0;
  }

  public static <A> A last(A[] a) {
    return l(a) != 0 ? a[l(a) - 1] : null;
  }

  public static <A> A last(Iterator<A> it) {
    A a = null;
    while (it.hasNext()) {
      ping();
      a = it.next();
    }
    return a;
  }

  public static String deSquareBracket(String s) {
    if (startsWith(s, "[") && endsWith(s, "]"))
      return substring(s, 1, l(s) - 1);
    return s;
  }

  public static <A> A popLast(List<A> l) {
    return liftLast(l);
  }

  public static Pair<String, String> splitTrailingSquareBracketStuff(String s) {
    if (!endsWith(s, "]"))
      return pair(s, "");
    int i = smartLastIndexOf(s, '[');
    return pair(trimSubstring(s, 0, i), substring(s, i + 1, l(s) - 1));
  }

  public static String gazelle_canonicalizeLineType(String lineType) {
    return or(matchAny_firstGroup("Gazelle line type " + quote(lineType) + " is rewritten to *.", gazelle_gazelleFacts()), lineType);
  }

  public static Object quickImport(Object o) {
    return quickExport(o, mc());
  }

  public static String dm_gazelle_rulesModule() {
    return dm_require("#1021352/Rules");
  }

  public static String newLinesToSpaces_trim(String s) {
    return s == null ? null : trim(s).replaceAll("\\s*\r?\n\\s*", " ");
  }

  public static String joinLinesWithComma(String s) {
    return joinWithComma(tlft(s));
  }

  public static <A> A setTLAndReturn(ThreadLocal<A> tl, A value) {
    return setThreadLocalAndReturn(tl, value);
  }

  public static boolean boolPar(ThreadLocal<Boolean> tl) {
    return boolOptParam(tl);
  }

  public static boolean boolPar(Object[] __, String name) {
    return boolOptParam(__, name);
  }

  public static boolean boolPar(String name, Object[] __) {
    return boolOptParam(__, name);
  }

  public static boolean boolPar(String name, Object[] params, boolean defaultValue) {
    return optParam(params, name, defaultValue);
  }

  public static JWindow infoBox(String text) {
    return infoMessage(text);
  }

  public static JWindow infoBox(String text, double seconds) {
    return infoMessage(text, seconds);
  }

  public static JWindow infoBox(Throwable e) {
    return infoMessage(e);
  }

  public static String rtrim_fromLines(Collection lines) {
    StringBuilder buf = new StringBuilder();
    if (lines != null) {
      boolean first = true;
      for (Object line : lines) {
        if (first)
          first = false;
        else
          buf.append('\n');
        buf.append(str(line));
      }
    }
    return buf.toString();
  }

  public static boolean startsWithIgnoreCase(String a, String b) {
    return regionMatchesIC(a, 0, b, 0, b.length());
  }

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

  public static <A> List<A> newSubList(List<A> l, int startIndex, int endIndex) {
    return cloneList(subList(l, startIndex, endIndex));
  }

  public static <A> List<A> newSubList(List<A> l, int startIndex) {
    return cloneList(subList(l, startIndex));
  }

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

  public static <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);
  }

  public static List collectField(Collection c, String field) {
    List l = new ArrayList();
    if (c != null)
      for (Object a : c) l.add(getOpt(a, field));
    return l;
  }

  public static List collectField(String field, Collection c) {
    return collectField(c, field);
  }

  public static GazelleEvalContext gazelle_cloneCtx(GazelleEvalContext ctx) {
    GazelleEvalContext ctx2 = shallowClone(ctx);
    ctx2.engine = new RuleEngine2();
    ctx2.engine.copyRulesFrom(ctx.engine);
    return ctx2;
  }

  public static HashMap litmap(Object... x) {
    HashMap map = new HashMap();
    litmap_impl(map, x);
    return map;
  }

  public static void litmap_impl(Map map, Object... x) {
    for (int i = 0; i < x.length - 1; i += 2) if (x[i + 1] != null)
      map.put(x[i], x[i + 1]);
  }

  public static TreeSet<String> asCISet(Iterable<String> c) {
    return toCaseInsensitiveSet(c);
  }

  public static TreeSet<String> asCISet(String... x) {
    return toCaseInsensitiveSet(x);
  }

  public static void addIfNempty(Collection<String> l, String s) {
    if (nempty(s))
      l.add(s);
  }

  public static void addIfNempty(Collection l, Map m) {
    if (nempty(m))
      l.add(m);
  }

  public static void addIfNempty(Collection l, Collection c) {
    if (nempty(c))
      l.add(c);
  }

  public static <A> A dm_callOpt(Object moduleOrID, String method, Object... args) {
    return dm_callModuleOpt(moduleOrID, method, args);
  }

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

  public static <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));
  }

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

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

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

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

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

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

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

  public static int max(Collection<Integer> c) {
    int x = Integer.MIN_VALUE;
    for (int i : c) x = max(x, i);
    return x;
  }

  public 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;
  }

  public 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;
  }

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

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

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

  public static <A> Collector<A> limitedCollector(Collection<A> l, int max) {
    return limitedListCollector(l, max);
  }

  public static F1 f_id_value;

  public static <A> F1<A, A> f_id() {
    if (f_id_value == null)
      f_id_value = new F1<A, A>() {

        public A get(A a) {
          try {
            return a;
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "ret a;";
        }
      };
    return f_id_value;
  }

  public static String gazelle_processSafeEvals(String text, Map<String, String> evalCache) {
    String l = dm_processEmbeddedSafeEvals(text, "evalCache", evalCache);
    if (l != text) {
      print("Processed eval: " + text + " => " + l);
      text = l;
    }
    return text;
  }

  public static TreeSet<String> litCISet(String... items) {
    return litciset(items);
  }

  public static Collection<String> gazelle_executeSplitterRule(RuleEngine2 engine, RuleEngine2.SimplifyWithRule rule, String input) {
    Object f = rule.function();
    Collection<String> out = (Collection<String>) ((f instanceof String ? callF(engine.callFunctionOnString, (String) f, input) : callF(f, input)));
    if (l(out) == 1 && eq(first(out), input))
      return emptyList();
    return out;
  }

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

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

  public static <A> List<A> takeFirst_clone(int n, Iterable<A> i) {
    List l = new ArrayList();
    Iterator<A> it = i.iterator();
    for (int _repeat_53 = 0; _repeat_53 < n; _repeat_53++) {
      if (it.hasNext())
        l.add(it.next());
      else
        break;
    }
    return l;
  }

  public static void gazelle_processSafeEvalsInTree(List<GazelleTree> tree) {
    Map<String, String> evalCache = new HashMap();
    if (tree != null)
      for (GazelleTree t : tree) {
        String l = dm_processEmbeddedSafeEvals(t.line, "evalCache", evalCache);
        if (l != t.line) {
          print("Processed eval: " + t.line + " => " + l);
          t.line = l;
        }
      }
  }

  public static String n2(long l) {
    return formatWithThousands(l);
  }

  public static String n2(Collection l) {
    return n2(l(l));
  }

  public static String n2(double l, String singular) {
    return n2(l, singular, singular + "s");
  }

  public static String n2(double l, String singular, String plural) {
    if (fraction(l) == 0)
      return n2((long) l, singular, plural);
    else
      return l + " " + plural;
  }

  public static String n2(long l, String singular, String plural) {
    return n_fancy2(l, singular, plural);
  }

  public static String n2(long l, String singular) {
    return n_fancy2(l, singular, singular + "s");
  }

  public static String n2(Collection l, String singular) {
    return n2(l(l), singular);
  }

  public static String n2(Collection l, String singular, String plural) {
    return n_fancy2(l, singular, plural);
  }

  public static String n2(Map m, String singular, String plural) {
    return n_fancy2(m, singular, plural);
  }

  public static String n2(Map m, String singular) {
    return n2(l(m), singular);
  }

  public static String n2(Object[] a, String singular) {
    return n2(l(a), singular);
  }

  public static String n2(Object[] a, String singular, String plural) {
    return n_fancy2(a, singular, plural);
  }

  public static String n2(MultiSet ms, String singular, String plural) {
    return n_fancy2(ms, singular, plural);
  }

  public static GazelleEvalContext dm_gazelle_stdEvalContext() {
    return dm_gazelle_stdEvalContext(dm_allRulesFromRulesModuleWithCommentsAndIDs());
  }

  public static GazelleEvalContext dm_gazelle_stdEvalContext(List<T3<String, String, String>> rules, Object... __) {
    GazelleEvalContext ctx = gazelle_stdEvalContext_v2(rules, __);
    ctx.engine.callFunctionOnString = new F2<String, String, Object>() {

      public Object get(String function, String s) {
        try {
          return dm_callFunctionOnString(function, s);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "dm_callFunctionOnString(function, s)";
      }
    };
    return ctx;
  }

  public static void gazelle_dropRulesNotOnInput(GazelleEvalContext ctx) {
    ctx.engine.dropRulesWhere(r -> cicOneOf(r.comments, "in 1 = statement", "in = statement", "disable"));
  }

  public static HashSet collectUnique(Collection c, String field) {
    HashSet set = new HashSet();
    for (Object a : c) {
      Object val = getOpt(a, field);
      if (val != null)
        set.add(val);
    }
    return set;
  }

  public static HashSet collectUnique(String field, Collection c) {
    return collectUnique(c, field);
  }

  public static void gazelle_addHelpers(GazelleEvalContext ctx, Object... __) {
    if (ctx.engine.hasHelpers)
      return;
    print("Adding helpers");
    ctx.engine.hasHelpers = true;
    gazelle_addHelperTablesToRules(ctx.engine);
    gazelle_addVarSplittersToRules(ctx.engine, __);
    gazelle_addVarDiscriminators(ctx.engine, __);
    gazelle_addVarConvertersToRules(ctx.engine, __);
    gazelle_addCalculationFunctionsToRules(ctx.engine, __);
    gazelle_addRespondToHumanOnlyHelpers(ctx.engine, __);
    gazelle_addForbiddenBindingHelpers(ctx.engine, __);
  }

  public static Object[] paramsMinus(Object[] a1, Object... keys) {
    return paramsWithout(a1, keys);
  }

  public static List<GazelleTree> gazelle_getChildren_withContinuations(GazelleTree tree, Object... __) {
    List<GazelleTree> l = gazelle_getChildren(tree, __);
    boolean debug = boolPar("debug", __) || boolPar("debugContinuations", __);
    Matches m = new Matches();
    boolean changes = false;
    for (int i = 0; i < l(l); i++) {
      GazelleTree t = l.get(i);
      {
        if (!(eq(t.lineType, "instruction") && match("continue with line * of rule * without variables", t.line, m)))
          continue;
      }
      if (debug)
        print("Processing continuation: " + sfu(m));
      String ruleID = m.unq(1);
      RuleEngine2.Rule rule = t.ctx.engine.getRule(ruleID);
      if (rule == null) {
        print("Follow-up rule not found: " + ruleID);
        {
          l.remove(i--);
          continue;
        }
      }
      changes = true;
      t.mr.rule = rule;
      t.mr.iCond = parseInt(m.unq(0)) - 1;
      t.mr.map = ciMap();
      if (!t.mr.moreConditions()) {
        t.line = rule.out;
        t.lineType = rule.outType();
      }
      gazelle_addPredictionToTreeNode(t);
    }
    if (changes)
      gazelle_sortChildren(l);
    return l;
  }

  public static void gazelle_sortChildren(GazelleTree tree) {
    if (tree == null)
      return;
    gazelle_sortChildren(tree.children);
  }

  public static void gazelle_sortChildren(List<GazelleTree> children) {
    if (children != null)
      sortByCalculatedFieldDesc(children, new F1<GazelleTree, Integer>() {

        public Integer get(GazelleTree c) {
          try {
            return ai_goodBadToInt(c.prediction);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "ai_goodBadToInt(c.prediction)";
        }
      });
  }

  public static Map<String, String> gazelle_inputMapFromLineAndHistory(String line, List<String> history) {
    Map<String, String> inputMap = litmap("standard", line);
    for (int i = 0; i < l(history); i++) inputMap.put("history-" + (l(history) - i), history.get(i));
    return inputMap;
  }

  public static String sfu(Object o) {
    return structureForUser(o);
  }

  public static <A, B> TreeMap<A, B> asTreeMap(Map<A, B> map) {
    return map instanceof TreeMap ? (TreeMap) map : new TreeMap(map);
  }

  public static boolean dm_gazelle_matchStep_statement(GazelleTree t, Object... __) {
    if (t.rule() == null)
      return false;
    boolean debug = boolPar("debug", __) || boolPar("matchStatementsDebug", __);
    RuleEngine2_MatchedRule mr = t.mr;
    List<String> in = t.rule().in;
    int iCond = t.mr.iCond;
    if (debug)
      print("matchStatements: iCond=" + iCond + " in " + in);
    if (iCond >= l(in))
      return false;
    List<String> conditions = dropFirst(iCond, in);
    if (debug)
      print("Remaining conditions for " + t.ruleID() + " with " + t.mr.map + ": " + conditions);
    Matches m = new Matches();
    if (!matchAny("in " + (iCond + 1) + " = statement *", t.rule().comments, m))
      return false;
    String subRuleID = m.unq(0);
    if (debug)
      print("Found subrule: " + subRuleID);
    List fulfillments = dm_gazelle_fulfillmentsForRule(subRuleID);
    Lowest<Map<String, String>> bestMap = new Lowest();
    if (debug)
      print("Have " + n2(fulfillments, "fulfillment") + ".");
    for (Object f : fulfillments) {
      Map<String, String> varMap = asCIMap(safeUnstructMap(getString("varMap", f)));
      if (debug)
        print("fulfillment => " + sfu(varMap));
      boolean selfVarsFix = boolPar("selfVarsFix", __, true);
      if (selfVarsFix) {
        String cond = first(conditions);
        for (String x : identifiersOnly(codeTokens(mr.tokenize(cond)))) if (!varMap.containsKey(x)) {
          if (debug)
            print("Self-linking " + x);
          varMap.put(x, x);
        }
      }
      if (debug)
        print("Original map: " + t.mr.map);
      Map<String, String> newMap = mergeMapsStrictlyIC(asCIMap(t.mr.map), varMap);
      if (newMap == null) {
        if (debug)
          printIndent("Can't merge maps");
        continue;
      }
      if (debug)
        printIndent("Maps merged! => " + sfu(newMap));
      newMap = ciMapWithoutKeysEqualToValues(newMap);
      if (debug)
        printIndent("Map simplified => " + sfu(newMap));
      {
        if (gazelle_isRecentlyUsedMapping(t, newMap, __))
          continue;
      }
      bestMap.put(newMap, l(newMap));
    }
    Map<String, String> newMap = bestMap.get();
    if (newMap == null) {
      if (debug)
        print("No matching statement found");
      return false;
    }
    t.mr.map = newMap;
    t.mr.iCond++;
    t.mr.remainingConditions = dropFirst(t.mr.remainingConditions);
    t.mr.remainingConditionsWithType = dropFirst(t.mr.remainingConditionsWithType);
    t.mr.out = null;
    t.line = t.mr.outText();
    if (debug)
      print("Rewrote " + t.mr.rule.out + " to: " + t.line);
    return true;
  }

  public static Object[] passParams(Object[] __, String... params) {
    Map map = paramsToMap(__);
    List l = new ArrayList();
    int n = l(l) - 1;
    for (String key : params) if (map.containsKey(key)) {
      l.add(key);
      l.add(map.get(key));
    }
    return empty(l) ? null : toObjectArray(l);
  }

  public static void dm_gazelle_matchStep_additionalInput(GazelleTree t, Map<String, String> inputsByType, Object... __) {
    if (t.rule() == null)
      return;
    boolean debug = boolPar("debug", __);
    TextAndType next = get(t.rule().insWithType, t.mr.iCond);
    if (next == null || !startsWith(next.type, "history-"))
      return;
    if (ai_ruleEngine2_matchStep(t.mr, inputsByType, true, __)) {
      if (debug)
        print("Input match OK. new map: " + t.mr.map + ". Updating prediction.");
      gazelle_addPredictionToTreeNode(t);
    } else if (debug)
      print("Input match failed");
  }

  public static void dm_gazelle_matchStep_condition(GazelleTree t, Object... __) {
    if (t.rule() == null)
      return;
    boolean debug = boolPar("debug", __) || boolPar("debugConditions", __);
    GRuleLine next = get(t.rule().insWithType, t.mr.iCond);
    if (debug)
      print("matchStep_condition: " + getString("type", next));
    if (next == null || !eq(next.type, "condition"))
      return;
    boolean satisfied = false;
    String cond = t.mr.applyMappingTo(next);
    if (gazelle_isEmbeddedEval(cond)) {
      if (debug)
        print("Embedded eval: " + cond);
      satisfied = eqic(dm_processEmbeddedSafeEvals(cond), "true");
    } else {
      GazelleEvalContext subCtx = (GazelleEvalContext) (callF(requiredPar("contextMaker", __)));
      subCtx.engine.dropRulesWhere(r -> neq(r.purpose, "condition to eval"));
      List<GazelleTree> l = dm_gazelle_reasonAboutChatInput_v2(null, cond, paramsPlus(paramsMinus(__, "acceptablePurposes"), "ctx", subCtx));
      gazelle_processSafeEvalsInTree(l);
      if (debug) {
        print("Sub-Tree for condition " + cond + ":");
        printIndentLines(l);
      }
      for (GazelleTree tree : l) if (eqic(tree.line, "true")) {
        satisfied = true;
        break;
      } else if (eqic(tree.line, "false"))
        break;
    }
    if (debug)
      print("satisfied=" + satisfied);
    if (!satisfied)
      return;
    t.mr.iCond++;
    gazelle_addPredictionToTreeNode(t);
    t.mr.remainingConditions = dropFirst(t.mr.remainingConditions);
    t.mr.remainingConditionsWithType = dropFirst(t.mr.remainingConditionsWithType);
  }

  public static List<GazelleTree> gazelle_dropUnsatisfiedEntries(List<GazelleTree> l) {
    return antiFilter(l, t -> t.mr != null && t.mr.moreConditions());
  }

  public static void gazelle_dropMatchedDefaultRules(List<GazelleTree> l, Object... __) {
    List<GazelleTree> l2 = filter(l, t -> !eqic(t.lineType, "temporary fact"));
    Set<String> trumpedRules = new HashSet();
    for (GazelleTree t : l2) addAll(trumpedRules, matchAll_first("trumps rule *", t.ruleComments()));
    List<GazelleTree> trumped = filter(l2, new F1<GazelleTree, Boolean>() {

      public Boolean get(GazelleTree t) {
        try {
          return contains(t.ruleComments(), "default") || trumpedRules.contains(t.ruleID());
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "contains(t.ruleComments(), \"default\")\r\n    || trumpedRules.contains(t.ruleID())";
      }
    });
    if (nempty(trumped) && l(trumped) < l(l2)) {
      if (boolPar("debug", __) || boolPar("debugDefaults", __)) {
        print("Removing default/trumped rules:");
        printIndentLines(trumped);
        print("Because there are also:");
        printIndentLines(listMinusSet(l2, trumped));
      }
      l.removeAll(trumped);
    }
  }

  public static <A, B extends A> void addAll(Collection<A> c, Iterable<B> b) {
    if (c != null && b != null)
      for (A a : b) c.add(a);
  }

  public static <A, B extends A> boolean addAll(Collection<A> c, Collection<B> b) {
    return c != null && b != null && c.addAll(b);
  }

  public static <A, B extends A> boolean addAll(Collection<A> c, B... b) {
    return c != null && c.addAll(Arrays.asList(b));
  }

  public static <A, B extends A> void addAll(Collector<A> c, Iterable<B> b) {
    if (c != null && b != null)
      for (A a : b) c.add(a);
  }

  public static List<GazelleTree> gazelle_reasonWithPreAndPost(String input, Object... __) {
    return gazelle_postprocess(gazelle_reasonWithPreprocessing(input, __), __);
  }

  public static void gazelle_sortBeforeCommit(List<GazelleTree> l, Object... __) {
    boolean debug = boolPar("debugSorting", __);
    sort(l, new Comparator<GazelleTree>() {

      public int compare(GazelleTree a, GazelleTree b) {
        if (debug)
          print("sorting: " + a.line + " / " + b.line);
        int c = ai_goodBadToInt(b.prediction) - ai_goodBadToInt(a.prediction);
        if (debug)
          print("  " + c);
        if (c != 0)
          return c;
        c = gazelle_historyLevelRequired(b.rule()) - gazelle_historyLevelRequired(a.rule());
        if (debug)
          print("  " + c);
        return c;
      }
    });
  }

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

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

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

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

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

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

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

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

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

  public static List<String> tok_splitAtPlusAtBeginningOfLine(String s) {
    List<Integer> indices = new ArrayList();
    List<String> tok = javaTokWithAllBrackets(s);
    for (int i = 1; i < l(tok); i += 2) if (eqGet(tok, i, "+") && containsNewLine(get(tok, i - 1)))
      indices.add(i);
    return splitAtTokenIndices(tok, indices);
  }

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

  public static <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;
  }

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

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

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

  public static <A> HashSet<A> asHashSet(Collection<A> c) {
    synchronized (collectionMutex(c)) {
      return new HashSet(c);
    }
  }

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

  public static List<String> mechList(String name) {
    return mechList_tlft(name);
  }

  public static boolean canQuickEval(String t) {
    return isQuoted(t) || isIdentifier(t) || isInteger(t) || isSnippetID(t);
  }

  public static Object quickEval(String t) {
    if (isQuoted(t))
      return unquote(t);
    if (isInteger(t))
      return parseIntOrLong(t);
    if (isLongConstant(t))
      return parseLong(t);
    if (eq(t, "null"))
      return null;
    if (eq(t, "false"))
      return false;
    if (eq(t, "true"))
      return true;
    if (isIdentifier(t))
      return getMC(t);
    if (isSnippetID(t))
      return t;
    throw quickFail("Can't quick eval: " + t);
  }

  public static Pair<String, List<String>> tok_parseFunctionCall(String s) {
    return tok_parseFunctionCall(javaTok(s));
  }

  public static Pair<String, List<String>> tok_parseFunctionCall(List<String> tok) {
    if (!(isIdentifier(get(tok, 1)) && eqGet(tok, 3, "(")))
      return null;
    Map<Integer, Integer> bracketMap = getBracketMap(tok);
    Integer iClosing = bracketMap.get(3);
    if (!(eq(iClosing, l(tok) - 4) && eqGet(tok, l(tok) - 2, ";")) && neq(iClosing, l(tok) - 2))
      return null;
    int i = 5, argStart = 5;
    List<String> args = new ArrayList();
    while (i < iClosing) {
      Integer j = bracketMap.get(i);
      if (j != null) {
        i = j + 2;
        continue;
      }
      if (eqGetOneOf(tok, i, ",")) {
        if (i > argStart)
          args.add(trimJoinSubList(tok, argStart, i));
        argStart = i + 2;
      }
      i += 2;
    }
    if (i > argStart)
      args.add(trimJoinSubList(tok, argStart, i));
    return pair(tok.get(1), args);
  }

  public static void shareMyCreatorWith(Object o) {
    setCreatorTo(o, creator());
  }

  public static <A> WeakReference<A> weakRef(A a) {
    return newWeakReference(a);
  }

  public static Object callAndMake(String functionName, Object... args) {
    return callAndMake_orDirect(functionName, args);
  }

  public static Object[] toObjectArray(Collection c) {
    List l = asList(c);
    return l.toArray(new Object[l.size()]);
  }

  public static Object nuObject(String className, Object... args) {
    try {
      return nuObject(classForName(className), args);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static <A> A nuObject(Class<A> c, Object... args) {
    try {
      if (args.length == 0)
        return nuObjectWithoutArguments(c);
      Constructor m = nuObject_findConstructor(c, args);
      m.setAccessible(true);
      return (A) m.newInstance(args);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static Constructor nuObject_findConstructor(Class c, Object... args) {
    for (Constructor m : c.getDeclaredConstructors()) {
      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!" : ""));
  }

  public static 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;
  }

  public static void setAll(Object o, Map<String, Object> fields) {
    if (fields == null)
      return;
    for (String field : keys(fields)) set(o, field, fields.get(field));
  }

  public static void setAll(Object o, Object... values) {
    failIfOddCount(values);
    for (int i = 0; i + 1 < l(values); i += 2) {
      String field = (String) values[i];
      Object value = values[i + 1];
      set(o, field, value);
    }
  }

  public static void saveGZStructToFileVerbose(File file, Object o) {
    saveGZStructureToFile(file, o);
    printFileSaved(file);
  }

  public static File gazelle_discord_reasoningFile(Gazelle_ReasoningForLine r) {
    return r.outMsgID != 0 ? gazelle_discord_reasoningFileForMsgID(r.outMsgID) : nempty(r.outContext) ? gazelle_discord_reasoningFileForContext(r.outContext) : null;
  }

  public static void gazelle_copyLineToLongAppliedRuleList(String context) {
    try {
      if (nempty(gazelle_applicationsForContext(context)))
        return;
      try {
        String mrStruct = gazelle_matchedRuleStructForContext(context);
        if (mrStruct == null)
          return;
        printIndent(shorten(mrStruct, 50));
        dm_gazelle_saveAppliedRuleToLongList("matchedRuleStruct", mrStruct, "context", context);
      } catch (Throwable _e) {
        print("Context: " + context);
        throw rethrow(_e);
      }
    } catch (Throwable __e) {
      _handleException(__e);
    }
  }

  public static void dm_discord_copyLineToLongAppliedRuleList(long msgID) {
    try {
      if (nempty(dm_gazelle_applicationsForMsgID(msgID)))
        return;
      try {
        String mrStruct = gazelle_discord_matchedRuleStructForMsgID(msgID);
        if (mrStruct == null)
          return;
        printIndent(shorten(mrStruct, 50));
        dm_gazelle_saveAppliedRuleToLongList("matchedRuleStruct", mrStruct, "context", "discord msg " + msgID);
      } catch (Throwable _e) {
        print("Msg ID: " + msgID);
        throw rethrow(_e);
      }
    } catch (Throwable __e) {
      _handleException(__e);
    }
  }

  public static String gazelle_rumCRUD() {
    return dm_require("#1022322/RecentlyUsedMappings");
  }

  public static String or2(String a, String b) {
    return nempty(a) ? a : b;
  }

  public static String or2(String a, String b, String c) {
    return or2(or2(a, b), c);
  }

  public static String stringPar(Object[] params, String name) {
    return stringOptPar(params, name);
  }

  public static String stringPar(String name, Object[] params) {
    return stringOptPar(params, name);
  }

  public static String stringPar(String name, Object[] params, String defaultValue) {
    return optPar(name, params, defaultValue);
  }

  public static <A> boolean eqGetOneOf(List<A> l, int i, A... options) {
    return eqOneOf(get(l, i), options);
  }

  public static void tokAppend(List<String> tok, int i, String s) {
    tok.set(i, tok.get(i) + s);
  }

  public static void removeSubList(List l, int from, int to) {
    if (l != null)
      subList(l, from, to).clear();
  }

  public static void removeSubList(List l, int from) {
    if (l != null)
      subList(l, from).clear();
  }

  public static volatile PersistableThrowable lastException_lastException;

  public static PersistableThrowable lastException() {
    return lastException_lastException;
  }

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

  public static Throwable getInnerException(Throwable e) {
    if (e == null)
      return null;
    while (e.getCause() != null) e = e.getCause();
    return e;
  }

  public static Throwable getInnerException(Runnable r) {
    return getInnerException(getException(r));
  }

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

  public static String hideCredentials(String url) {
    return url.replaceAll("([&?])(_pass|key)=[^&\\s\"]*", "$1$2=<hidden>");
  }

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

  public static String baseClassName(String className) {
    return substring(className, className.lastIndexOf('.') + 1);
  }

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

  public static String prependIfNempty(String prefix, String s) {
    return empty(s) ? s : prefix + s;
  }

  public static String getChannelName() {
    return (String) callOpt(getMainBot(), "getChannelName");
  }

  public static Object pcallFunction(Object f, Object... args) {
    try {
      return callFunction(f, args);
    } catch (Throwable __e) {
      _handleException(__e);
    }
    return null;
  }

  public static TimerTask timerTask(final Object r, final java.util.Timer timer) {
    return new TimerTask() {

      public void run() {
        if (!licensed())
          timer.cancel();
        else
          pcallF(r);
      }
    };
  }

  public static long toMS(double seconds) {
    return (long) (seconds * 1000);
  }

  public static boolean isAbsoluteURL(String s) {
    return contains(s, "://");
  }

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

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

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

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

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

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

  public static List<GazelleTree> dm_gazelle_reasonAbout(String line, Object... __) {
    return dm_gazelle_reasonAboutChatInput_v2(null, line, __);
  }

  public static <A, B> Map<A, B> mapPlus(Map<A, B> m, Object... data) {
    m = cloneMap(m);
    litmap_impl(m, data);
    return m;
  }

  public static Object[] assertEvenLength(Object[] a) {
    assertTrue(even(l(a)));
    return a;
  }

  public static LinkedHashMap paramsToOrderedMap(Object... params) {
    return asLinkedHashMap(paramsToMap(params));
  }

  public static Object[] mapToParams(Map map) {
    return mapToObjectArray(map);
  }

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

  public static 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);
    }
  }

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

  public static 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);
    }
  }

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

  public static Object[] massageArgsForVarArgsCall(Method m, Object[] args) {
    Class<?>[] types = m.getParameterTypes();
    int n = types.length - 1, nArgs = args.length;
    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);
    Object[] varArgs = arrayOfType(varArgType, nArgs - n);
    arraycopy(args, n, varArgs, 0, nArgs - n);
    newArgs[n] = varArgs;
    return newArgs;
  }

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

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

  public static Class __javax;

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

  public static List<String> splitAtEllipsis(String s) {
    return splitAtTokens(javaTokWithBrackets(s), ".", "", ".", "", ".");
  }

  public static String getStackTrace2(Throwable e) {
    return hideCredentials(getStackTrace(unwrapTrivialExceptionWraps(e)) + replacePrefix("java.lang.RuntimeException: ", "FAIL: ", hideCredentials(str(innerException2(e)))) + "\n");
  }

  public static String formatSnippetIDOpt(String s) {
    return isSnippetID(s) ? formatSnippetID(s) : s;
  }

  public static String formatSnippetID(String id) {
    return "#" + parseSnippetID(id);
  }

  public static String formatSnippetID(long id) {
    return "#" + id;
  }

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

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

  public static byte[] bytesFromHex(String s) {
    return hexToBytes(s);
  }

  public static String dropPrefix(String prefix, String s) {
    return s == null ? null : s.startsWith(prefix) ? s.substring(l(prefix)) : s;
  }

  public static int ubyteToInt(byte b) {
    return b & 0x0FF;
  }

  public static Comparator<String> caseInsensitiveComparator() {
    return betterCIComparator();
  }

  public static TreeSet<String> toCaseInsensitiveSet(Iterable<String> c) {
    if (isCISet(c))
      return (TreeSet) c;
    TreeSet<String> set = caseInsensitiveSet();
    addAll(set, c);
    return set;
  }

  public static TreeSet<String> toCaseInsensitiveSet(String... x) {
    TreeSet<String> set = caseInsensitiveSet();
    addAll(set, x);
    return set;
  }

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

  public static Object vmBus_query(String msg, Object... args) {
    Object arg = vmBus_wrapArgs(args);
    {
      Object __444 = pcallFAll_returnFirstNotNull(vm_busListeners_live(), msg, arg);
      if (__444 != null)
        return __444;
    }
    return pcallFAll_returnFirstNotNull(vm_busListenersByMessage_live().get(msg), msg, arg);
  }

  public static Object vmBus_query(String msg) {
    return vmBus_query(msg, (Object) null);
  }

  public static File getProgramDir() {
    return programDir();
  }

  public static File getProgramDir(String snippetID) {
    return programDir(snippetID);
  }

  public static File programFile(String name) {
    return prepareProgramFile(name);
  }

  public static File programFile(String progID, String name) {
    return prepareProgramFile(progID, name);
  }

  public static SimpleDateFormat simpleDateFormat_local(String format) {
    SimpleDateFormat sdf = new SimpleDateFormat(format);
    sdf.setTimeZone(localTimeZone());
    return sdf;
  }

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

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

  public static String dm_requireModule(String moduleLibID) {
    return dm_makeModule(moduleLibID);
  }

  public static boolean useLocalMechListCopies_flag;

  public static boolean useLocalMechListCopies() {
    return useLocalMechListCopies_flag;
  }

  public static void useLocalMechListCopies(boolean flag) {
    useLocalMechListCopies_flag = flag;
    if (flag)
      mechMode().readMode = "localCopies";
  }

  public static void setOptMC(String field, Object value) {
    setOpt(mc(), field, value);
  }

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

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

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

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

  public static <A> Set<A> synchroHashSet() {
    return Collections.synchronizedSet(new HashSet<A>());
  }

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

  public static <A> void addIfNotNull(MultiSet<A> ms, A a) {
    if (a != null && ms != null)
      ms.add(a);
  }

  public static void closeAutoCloseables(Collection<AutoCloseable> l) {
    if (l != null)
      for (AutoCloseable c : l) {
        try {
          c.close();
        } catch (Throwable __e) {
          _handleException(__e);
        }
      }
  }

  public static <A> List<A> getAndClearList(Collection<A> l) {
    if (l == null)
      return emptyList();
    synchronized (collectionMutex(l)) {
      List<A> out = cloneList(l);
      l.clear();
      return out;
    }
  }

  public static String _userHome;

  public static String userHome() {
    if (_userHome == null) {
      if (isAndroid())
        _userHome = "/storage/sdcard0/";
      else
        _userHome = System.getProperty("user.home");
    }
    return _userHome;
  }

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

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

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

  public static ThreadLocal<VF1<File>> checkFileNotTooBigToRead_tl = new ThreadLocal();

  public static void checkFileNotTooBigToRead(File f) {
    callF(checkFileNotTooBigToRead_tl.get(), f);
  }

  public static Object collectionMutex(Object o) {
    String c = className(o);
    if (eq(c, "java.util.TreeMap$KeySet"))
      c = className(o = getOpt(o, "m"));
    else if (eq(c, "java.util.HashMap$KeySet"))
      c = className(o = get_raw(o, "this$0"));
    if (eqOneOf(c, "java.util.TreeMap$AscendingSubMap", "java.util.TreeMap$DescendingSubMap"))
      c = className(o = get_raw(o, "m"));
    return o;
  }

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

  public static String dm_makeModule(String moduleLibID) {
    return (String) dm_callOS("makeModule", moduleLibID);
  }

  public static boolean isString(Object o) {
    return o instanceof String;
  }

  public static boolean isIdentifier(String s) {
    return isJavaIdentifier(s);
  }

  public static Object dm_getService(String serviceName) {
    return empty(serviceName) ? null : dm_getModule(vmBus_query(assertIdentifier(serviceName)));
  }

  public static boolean isStringOrIntOrLong(Object o) {
    return o instanceof String || o instanceof Integer || o instanceof Long;
  }

  public static Object dm_callOS(String functionName, Object... args) {
    return call(dm_os(), functionName, args);
  }

  public static Object dm_resolveModule(Object moduleOrStem) {
    return dm_callOS("resolveModule", moduleOrStem);
  }

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

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

  public static String gazelle_matchedRuleStructFromReasoningForLine(Object reasoningForLine) {
    Object tree = getOpt(reasoningForLine, "tree");
    Object matchedRule = getOpt(tree, "mr");
    return structOrNull(matchedRule);
  }

  public static Object unstructGZFile(File f) {
    return unstructureGZFile(f);
  }

  public static File gazelle_discord_reasoningFileForMsgID(long msgID) {
    return newFile(gazelle_discordReasoningsDir(), msgID + ".gz");
  }

  public static String indentx(String s) {
    return indentx(indent_default, s);
  }

  public static String indentx(int n, String s) {
    return dropSuffix(repeat(' ', n), indent(n, s));
  }

  public static String indentx(String indent, String s) {
    return dropSuffix(indent, indent(indent, s));
  }

  public static TreeSet<String> ciSet() {
    return caseInsensitiveSet();
  }

  public static boolean makeAndroid3_disable;

  public static class Android3 {

    public String greeting;

    public boolean publicOverride;

    public int startPort = 5000;

    public Responder responder;

    public boolean console = true;

    public boolean quiet;

    public boolean daemon = false;

    public boolean incomingSilent = false;

    public int incomingPrintLimit = 200;

    public boolean useMultiPort = true;

    public boolean recordHistory;

    public boolean verbose;

    public int answerPrintLimit = 500;

    public boolean newLineAboveAnswer, newLineBelowAnswer;

    public int port;

    public long vport;

    public DialogHandler handler;

    public ServerSocket server;

    public Android3(String greeting) {
      this.greeting = greeting;
    }

    public Android3() {
    }

    public synchronized 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) {
          _handleException(__e);
        }
      }
    }

    public String toString() {
      return "Bot: " + greeting + " [vport " + vport + "]";
    }
  }

  public abstract static class Responder {

    public abstract String answer(String s, List<String> history);
  }

  public static Android3 makeAndroid3(final String greeting) {
    return makeAndroid3(new Android3(greeting));
  }

  public static Android3 makeAndroid3(final String greeting, Responder responder) {
    Android3 android = new Android3(greeting);
    android.responder = responder;
    return makeAndroid3(android);
  }

  public static 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;
  }

  public static 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...";
      }
    });
  }

  public static 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);
          }
        }
      }
    };
  }

  public static 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;
  }

  public static 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;
  }

  public static 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;
  }

  public static 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;
      }
    };
  }

  public static ThreadLocal<DialogIO> makeAndroid3_io = new ThreadLocal();

  public static Android3 makeAndroid3() {
    return makeAndroid3(getProgramTitle() + ".");
  }

  public static int makeBot(String greeting) {
    return makeAndroid3(greeting).port;
  }

  public static Android3 makeBot(Android3 a) {
    makeAndroid3(a);
    return a;
  }

  public static Android3 makeBot(String greeting, Object responder) {
    Android3 a = new Android3(greeting);
    a.responder = makeResponder(responder);
    makeBot(a);
    return a;
  }

  public static Android3 makeBot() {
    return makeAndroid3(defaultBotName());
  }

  public static long toLong(Object o) {
    if (o instanceof Number)
      return ((Number) o).longValue();
    if (o instanceof String)
      return parseLong((String) o);
    return 0;
  }

  public static Object dm_getStem(Object moduleOrID) {
    if (isStringOrIntOrLong(moduleOrID))
      return dm_getStemByID(moduleOrID);
    return or(getOpt(dm_getModule(moduleOrID), "_host"), moduleOrID);
  }

  public static <A, B> A foldl(F2<A, B, A> f, A seed, Iterable<B> l) {
    A a = seed;
    if (l != null)
      for (B b : l) a = callF(f, a, b);
    return a;
  }

  public static AutoCloseable dm_ownResource(AutoCloseable resource) {
    dm_currentModuleMandatory().ownResource(resource);
    return resource;
  }

  public static <A> AutoCloseable vmBus_addListener_basic(String msg, Object listener) {
    Map<String, Set> map = vm_busListenersByMessage_live();
    synchronized (map) {
      Set listeners = map.get(msg);
      if (listeners == null)
        map.put(msg, listeners = syncIdentityHashSet());
      return tempAdd(listeners, listener);
    }
  }

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

  public static List<T3<String, String, String>> dm_allRulesFromRulesModuleWithCommentsAndIDs() {
    return collectThreeFieldsAsTriple("text", "comments", "globalID", (List) dm_call(dm_gazelle_rulesModule(), "concepts"));
  }

  public static List<String> javaTokPlusBrackets(String s) {
    return tok_combineRoundOrCurlyBrackets_keep(javaTok(s));
  }

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

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

  public static <A> int indexOfSubList(List<A> x, List<A> y) {
    return indexOfSubList(x, y, 0);
  }

  public static <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;
  }

  public static <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;
  }

  public static String trimJoin(List<String> s) {
    return trim(join(s));
  }

  public static List<String> tok_splitAtVerticalBar(String s) {
    if (!contains(s, '|'))
      return ll(s);
    return splitAtTokens(javaTokPlusBrackets2(s), tok_splitAtVerticalBar_list);
  }

  public static List<String> tok_splitAtVerticalBar_list = ll("|");

  public static boolean matchX(String pat, String s) {
    return matchX(pat, s, null);
  }

  public static boolean matchX(String pat, String s, Matches m) {
    if (endsWith(pat, "..."))
      return matchStartX(pat, s, m);
    if (startsWith(pat, "..."))
      return matchEndX(pat, s, m);
    return match(pat, s, m);
  }

  public static Object dm_requireAndCallModule(String moduleLibID, String method, Object... args) {
    return dm_callModule(dm_requireModule(moduleLibID), method, args);
  }

  public static Map<String, java.util.regex.Pattern> compileRegexp_cache = syncMRUCache(10);

  public static 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;
  }

  public static List lazilyMap(final Object f, final List l) {
    return new RandomAccessAbstractList() {

      public final int size = l(l);

      public HashMap<Integer, Object> data = new HashMap();

      public int size() {
        return size;
      }

      public Object get(int i) {
        if (data.containsKey(i))
          return data.get(i);
        Object o = callF(f, l.get(i));
        data.put(i, o);
        return o;
      }
    };
  }

  public static boolean checkCondition(Object condition, Object... args) {
    return isTrue(callF(condition, args));
  }

  public static boolean gazelle_isEvalLine(GazelleLine line) {
    return line != null && gazelle_isEvalCmd(line.text);
  }

  public static char lastChar(String s) {
    return empty(s) ? '\0' : s.charAt(l(s) - 1);
  }

  public static Map<String, String> codeAnalysis_identifierSafetyMap() {
    return mechHashMap("JavaX Identifier Safety");
  }

  public static Set<String> tok_allIdentifiers(String s) {
    return new HashSet<String>(filter("isIdentifier", javaTokC(s)));
  }

  public static boolean codeAnalysis_isSafeIdentifier(String id) {
    return startsWith(id, "$");
  }

  public static List<String> tokSplitAtComma(String s) {
    List<String> tok = javaTok(s);
    List<String> l = new ArrayList();
    while (l(tok) > 1) {
      int i = smartIndexOf(tok, ",");
      l.add(trim(join(subList(tok, 0, i))));
      tok = subList(tok, i + 1);
    }
    return l;
  }

  public static Collection<String> simplifySafetyTags(Collection<String> tags) {
    List<String> l = uniquifyList(tags);
    if (l.contains("safe") && containsAnythingNeq(l, "safe"))
      return listMinus(l, "safe");
    return l;
  }

  public static String simplifySafetyTags(String tags) {
    List<String> l = tokSplitAtComma(tags);
    Collection<String> l2 = simplifySafetyTags(l);
    return l(l2) != l(l) ? joinWithComma(l2) : tags;
  }

  public static <A> A optParam(ThreadLocal<A> tl, A defaultValue) {
    return optPar(tl, defaultValue);
  }

  public static <A> A optParam(ThreadLocal<A> tl) {
    return optPar(tl);
  }

  public static <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;
  }

  public static Object optParam(Object[] opt, String name) {
    return optParam(opt, name, null);
  }

  public static Object dm_moduleOfType(String type) {
    return first(dm_modulesOfType(type));
  }

  public static int toMS_int(double seconds) {
    return toInt_checked((long) (seconds * 1000));
  }

  public static Object evalWithTimeoutOrException(final Object f, int timeoutMS) {
    return evalWithTimeoutOrException(timeoutMS, f);
  }

  public static Object evalWithTimeoutOrException(int timeoutMS, final Object f) {
    return eitherAOpt(evalWithTimeout(timeoutMS, f));
  }

  public static Object evalWithTimeoutOrException(double timeoutSeconds, final Object f) {
    return eitherAOpt(evalWithTimeout(timeoutSeconds, f));
  }

  public static <A> A evalWithTimeoutOrException(int timeoutMS, F0<A> f) {
    Either<Object, Thread> e = evalWithTimeout(timeoutMS, f);
    if (e.isA())
      return (A) e.a();
    throw fail("Timeout after " + timeoutMS + " ms");
  }

  public static <A> A evalWithTimeoutOrException(double timeoutSeconds, F0<A> f) {
    return evalWithTimeoutOrException(toMS(timeoutSeconds), f);
  }

  public static Object dm_evalJava_veryQuickJava;

  public static Object dm_evalJava_withModule(String expression, Object... args) {
    Object m = dm_current_generic();
    String code = evalJava_prep(expression) + "\r\n    sO dm_current_mandatory_generic_value;\r\n    \r\n    sO dm_current_mandatory_generic() { ret dm_current_mandatory_generic_value; }\r\n    sO dm_current_generic() { ret dm_current_mandatory_generic(); }\r\n  ";
    Object obj;
    if (dm_evalJava_veryQuickJava != null)
      obj = callF(dm_evalJava_veryQuickJava, code);
    else
      obj = veryQuickJava(code);
    setOpt(obj, "programID", programID());
    makeDependent(obj);
    setOpt(obj, "creator_class", newWeakReference(dm_os()));
    set(obj, "dm_current_mandatory_generic_value", m);
    return evalJava_myProgramID_main_2(obj, args);
  }

  public static int indexOfIgnoreCase(List<String> a, String b) {
    return indexOfIgnoreCase(a, b, 0);
  }

  public static int indexOfIgnoreCase(List<String> a, String b, int i) {
    int n = l(a);
    for (; i < n; i++) if (eqic(a.get(i), b))
      return i;
    return -1;
  }

  public static int indexOfIgnoreCase(String a, String b) {
    return indexOfIgnoreCase_manual(a, b);
  }

  public static boolean isCurlyBraced(String s) {
    List<String> tok = tok_combineCurlyBrackets_keep(javaTok(s));
    return l(tok) == 3 && startsWithAndEndsWith(tok.get(1), "{", "}");
  }

  public static int numberOfJavaTokens(String s) {
    return l(javaTokC(s));
  }

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

  public static String joinSubList(List<String> l, int i, int j) {
    return join(subList(l, i, j));
  }

  public static String joinSubList(List<String> l, int i) {
    return join(subList(l, i));
  }

  public static String remoteMechListMirrorStatus(String listName) {
    return (String) mapGet(safeUnstructMap(loadTextFile(remoteMechListMirrorMetaFile(listName))), "Status");
  }

  public static boolean matchStart(String pat, String s) {
    return matchStart(pat, s, null);
  }

  public static boolean matchStart(String pat, String s, Matches matches) {
    if (s == null)
      return false;
    return matchStart(pat, parse3_cachedInput(s), matches);
  }

  public static 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] = join(toks.subList(tokpat.size(), toks.size()));
    }
    return true;
  }

  public static String dropSuffix(String suffix, String s) {
    return s.endsWith(suffix) ? s.substring(0, l(s) - l(suffix)) : s;
  }

  public static 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);
  }

  public static String javaTok_substringC(String s, int i, int j) {
    return s.substring(i, j);
  }

  public static 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;
  }

  public static boolean javaTokWithExisting_isCopyable(String t, String s, int i, int j) {
    return t.length() == j - i && s.regionMatches(i, t, 0, j - i);
  }

  public static Map<String, String[]> javaTokForJFind_array_cache = synchronizedMRUCache(1000);

  public static 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;
  }

  public static 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;
  }

  public static <A> ArrayList<A> litlist(A... a) {
    ArrayList l = new ArrayList(a.length);
    for (A x : a) l.add(x);
    return l;
  }

  public static <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;
      removeSubList(l, i, i + l(x));
      l.addAll(i, y);
      i += l(y);
    }
    return l;
  }

  public static <A> List<A> replaceSublist(List<A> l, int fromIndex, int toIndex, List<A> y) {
    removeSubList(l, fromIndex, toIndex);
    l.addAll(fromIndex, y);
    return l;
  }

  public static boolean isQuoted(String s) {
    if (isNormalQuoted(s))
      return true;
    return isMultilineQuoted(s);
  }

  public static 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;
  }

  public static boolean checkTokCondition(Object condition, List<String> tok, int i) {
    if (condition instanceof TokCondition)
      return ((TokCondition) condition).get(tok, i);
    return checkCondition(condition, tok, i);
  }

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

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

  public static <A> void replaceCollection(Collection<A> dest, Collection<A> src) {
    dest.clear();
    dest.addAll(src);
  }

  public static void replaceListPart(List l, int i, int j, List l2) {
    int j2 = i + l(l2);
    if (j2 == j) {
      copyListPart(l2, 0, l, i, l(l2));
      return;
    }
    l.subList(i, j).clear();
    l.addAll(i, l2);
  }

  public static List<String> toLinesFullTrim(String s) {
    List<String> l = new ArrayList();
    for (String line : toLines(s)) if (nempty(line = trim(line)))
      l.add(line);
    return l;
  }

  public static List<String> toLinesFullTrim(File f) {
    List<String> l = new ArrayList();
    for (String line : linesFromFile(f)) if (nempty(line = trim(line)))
      l.add(line);
    return l;
  }

  public static <A> A liftLast(List<A> l) {
    if (l.isEmpty())
      return null;
    int i = l(l) - 1;
    A a = l.get(i);
    l.remove(i);
    return a;
  }

  public static int smartLastIndexOf(String s, char c) {
    if (s == null)
      return 0;
    int i = s.lastIndexOf(c);
    return i >= 0 ? i : l(s);
  }

  public static String matchAny_firstGroup(String pat, Iterable<String> l) {
    Matches m = new Matches();
    return matchAny(pat, l, m) ? m.unq(0) : null;
  }

  public static Collection<String> gazelle_gazelleFacts() {
    return mL("Gazelle Facts");
  }

  public static Object quickExport(Object o, Object dest) {
    return quickExport_impl(o, dest, new IdentityHashMap(), new HashMap());
  }

  public static Object quickExport(Object o, Object dest, int expectedNumberOfObjects) {
    return quickExport_impl(o, dest, new IdentityHashMap(expectedNumberOfObjects), new HashMap());
  }

  public static Object quickExport_impl(Object o, Object dest, IdentityHashMap seen, HashMap<Class, Class> classMap) {
    try {
      if (o == null || o instanceof String || o instanceof Number)
        return o;
      Object oo = seen.get(o);
      if (oo != null)
        return oo;
      if (o instanceof Object[]) {
        Object[] l = (Object[]) o;
        Object[] destO = newObjectArrayOfSameType(l, l.length);
        seen.put(o, destO);
        for (int i = 0; i < l.length; i++) destO[i] = quickExport_impl(l[i], dest, seen, classMap);
        return destO;
      }
      if (o instanceof List) {
        List l = (List) o;
        List destO = new ArrayList(l.size());
        seen.put(o, destO);
        for (int i = 0; i < l.size(); i++) destO.add(quickExport_impl(l.get(i), dest, seen, classMap));
        return destO;
      }
      if (o instanceof Map) {
        Map m = (Map) o;
        Map destO = new HashMap();
        seen.put(o, destO);
        for (Object e : ((Map) o).entrySet()) destO.put(quickExport_impl(((Map.Entry) e).getKey(), dest, seen, classMap), quickExport_impl(((Map.Entry) e).getValue(), dest, seen, classMap));
        return destO;
      }
      Class c = o.getClass();
      String className = c.getName();
      if (className.startsWith("main$")) {
        Class destClass = classMap.get(c);
        if (destClass == null) {
          if (!classMap.containsKey(c)) {
            if (!isAnonymousClassName(className))
              destClass = getClass_vmName(dest, className);
            classMap.put(c, destClass);
          }
          if (destClass == null)
            return o;
        }
        if (c == destClass)
          return o;
        Object destO = nuObjectWithoutArguments(destClass);
        seen.put(o, destO);
        while (c != Object.class) {
          Field[] fields = c.getDeclaredFields();
          for (Field field : fields) {
            if ((field.getModifiers() & Modifier.STATIC) != 0)
              continue;
            field.setAccessible(true);
            Object value = field.get(o);
            setOpt(destO, field.getName(), quickExport_impl(value, dest, seen, classMap));
          }
          c = c.getSuperclass();
        }
        return destO;
      }
      return o;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

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

  public static boolean boolOptParam(ThreadLocal<Boolean> tl) {
    return isTrue(optPar(tl));
  }

  public static boolean boolOptParam(Object[] __, String name) {
    return isTrue(optParam(__, name));
  }

  public static boolean boolOptParam(String name, Object[] __) {
    return boolOptParam(__, name);
  }

  public static boolean infoMessage_alwaysOnTop = true;

  public static double infoMessage_defaultTime = 5.0;

  public static JWindow infoMessage(String text) {
    return infoMessage(text, infoMessage_defaultTime);
  }

  public static JWindow infoMessage(final String text, final double seconds) {
    printHidingCredentials(text);
    return infoMessage_noprint(text, seconds);
  }

  public static JWindow infoMessage_noprint(String text) {
    return infoMessage_noprint(text, infoMessage_defaultTime);
  }

  public static JWindow infoMessage_noprint(final String _text, final double seconds) {
    final String text = hideCredentials(_text);
    if (empty(text))
      return null;
    logQuotedWithDate(infoBoxesLogFile(), text);
    if (isHeadless())
      return null;
    return (JWindow) swingAndWait(new F0<Object>() {

      public Object get() {
        try {
          final JWindow window = showWindow(infoMessage_makePanel(text));
          window.setSize(300, 150);
          moveToTopRightCorner(window);
          if (infoMessage_alwaysOnTop)
            window.setAlwaysOnTop(true);
          window.setVisible(true);
          if (seconds != 0)
            disposeWindowAfter(iround(seconds * 1000), window);
          return window;
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "final JWindow window = showWindow(infoMessage_makePanel(text));\r\n    window.s...";
      }
    });
  }

  public static JWindow infoMessage(Throwable e) {
    printStackTrace(e);
    return infoMessage(exceptionToStringShort(e));
  }

  public static boolean regionMatchesIC(String a, int offsetA, String b, int offsetB, int len) {
    return a != null && a.regionMatches(true, offsetA, b, offsetB, len);
  }

  public static <A> A shallowClone(A o) {
    return (A) shallowClone_impl(o);
  }

  public static Object shallowClone_impl(Object o) {
    if (o == null)
      return o;
    if (o instanceof List)
      return cloneList((List) o);
    if (o instanceof Map)
      return cloneMap(((Map) o));
    if (o instanceof String || o instanceof Number || o instanceof Boolean)
      return o;
    if (o instanceof Object[]) {
      Object[] l = (Object[]) o;
      return l.clone();
    }
    Class c = o.getClass();
    final Object clone = nuEmptyObject(c);
    copyFields(o, clone);
    return clone;
  }

  public static <A> A dm_callModuleOpt(Object moduleOrID, String method, Object... args) {
    Object mod = dm_getModule(moduleOrID);
    if (mod == null)
      return null;
    AutoCloseable __180 = dm_enter(mod);
    try {
      return (A) callOpt_withVarargs(mod, method, args);
    } finally {
      _close(__180);
    }
  }

  public static <A> Collector<A> limitedListCollector(final Collection<A> l, final int max) {
    return new Collector<A>() {

      public boolean full() {
        return l(l) >= max;
      }

      public boolean add(A a) {
        if (full())
          return false;
        l.add(a);
        return full();
      }

      public boolean contains(A a) {
        return l.contains(a);
      }
    };
  }

  public static String dm_processEmbeddedSafeEvals(String s, Object... __) {
    Map<String, String> evalCache = (Map<String, String>) (optPar("evalCache", __));
    if (!contains(s, "{="))
      return s;
    List<String> tok = javaTok(s);
    Map<Integer, Integer> bracketMap = curlyBracketMap(tok);
    for (int i = 1; i < l(tok) - 4; i += 2) {
      {
        if (!(eq(tok.get(i), "{") && eqGet(tok, i + 1, "") && eqGet(tok, i + 2, "=")))
          continue;
      }
      int j = or(bracketMap.get(i), l(tok));
      String code = joinSubList(tok, i + 4, j - 1);
      if (!isSafeCodeFragment(code))
        replaceTokens(tok, i + 2, i + 4, "UNSAFE: ");
      else {
        String result = mapGet(evalCache, code);
        if (result == null) {
          print("Evaluating: " + code);
          try {
            result = strOrEmpty(dm_javaEvalOrInterpret(code));
          } catch (Throwable e) {
            printStackTrace(e);
            result = "";
          }
          mapPut(evalCache, code, result);
        }
        replaceTokens(tok, i, j + 1, result);
      }
      i = j - 2;
    }
    return trim(join(tok));
  }

  public static <A> List<A> cloneSubList(List<A> l, int startIndex, int endIndex) {
    return newSubList(l, startIndex, endIndex);
  }

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

  public static String formatWithThousands(long l) {
    return formatWithThousandsSeparator(l);
  }

  public static double fraction(double d) {
    return d % 1;
  }

  public static String n_fancy2(long l, String singular, String plural) {
    return formatWithThousandsSeparator(l) + " " + trim(l == 1 ? singular : plural);
  }

  public static String n_fancy2(Collection l, String singular, String plural) {
    return n_fancy2(l(l), singular, plural);
  }

  public static String n_fancy2(Map m, String singular, String plural) {
    return n_fancy2(l(m), singular, plural);
  }

  public static String n_fancy2(Object[] a, String singular, String plural) {
    return n_fancy2(l(a), singular, plural);
  }

  public static String n_fancy2(MultiSet ms, String singular, String plural) {
    return n_fancy2(l(ms), singular, plural);
  }

  public static GazelleEvalContext gazelle_stdEvalContext_v2(List<T3<String, String, String>> rules, Object... __) {
    long startTime = sysNow();
    GazelleEvalContext ctx = new GazelleEvalContext();
    ctx.engine = new RuleEngine2();
    ctx.engine.addRules2(rules);
    gazelle_changeInTypesForRulesWithMultipleInputs(ctx.engine);
    MultiSet<String> statements = dm_gazelle_statementsFromAppliedRules_multiSet();
    ctx.dataByRule = new HashMap();
    Map<String, MultiSet<String>> statementsByRule = ai_gazelle_indexStatementsByRule_multiSet(statements);
    for (String ruleID : keys(statementsByRule)) {
      MultiSet<String> statements2 = statementsByRule.get(ruleID);
      ctx.dataByRule.put(ruleID, ai_gazelle_analyzeStatementsForRule_multiSet(statements2));
    }
    ctx.predictor = dm_gazelle_standardPredictor(__);
    done2(startTime, "Making engine with " + n2(rules, "rule"));
    return ctx;
  }

  public static Object dm_callFunctionOnString(String code, String s) {
    return callF(dm_codeWithHintsToFunctionOnString(code), s);
  }

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

  public static boolean cicOneOf(Collection<String> l, String... bla) {
    for (String x : unnull(l)) if (eqicOneOf(x, bla))
      return true;
    return false;
  }

  public static class gazelle_addHelperTablesToRules_Massager {

    public List<List<String>> helperTable;

    public Map<String, Pair<Integer, Integer>> helperIndex;

    public gazelle_addHelperTablesToRules_Massager() {
    }

    public gazelle_addHelperTablesToRules_Massager(List<List<String>> helperTable, Map<String, Pair<Integer, Integer>> helperIndex) {
      this.helperIndex = helperIndex;
      this.helperTable = helperTable;
    }

    public Map<String, String> get(Map<String, String> map, List<String> tokC, List<String> tokI, RuleEngine2_MatchedRule matched) {
      if (map == null)
        return null;
      for (String t : codeTokens(matched.tokenize(matched.rule.out))) {
        if (map.containsKey(t))
          continue;
        Pair<Integer, Integer> idx = helperIndex.get(t);
        if (idx == null)
          continue;
        {
          final Map<String, String> __2871 = cloneMap(map);
          for (String key : keys(__2871)) {
            String val = __2871.get(key);
            int idx2 = indexOfIC(helperTable.get(idx.a), key);
            if (idx2 < 0)
              continue;
            Pair<Integer, Integer> idxVal = helperIndex.get(val);
            if (idxVal == null)
              continue;
            String out = _get(_get(helperTable, idxVal.a), idx.b);
            print("Massaged: " + t + " <-> " + key + " => " + out);
            mapPut(map, t, out);
          }
        }
      }
      return map;
    }
  }

  public static void gazelle_addHelperTablesToRules(RuleEngine2 engine) {
    Matches m = new Matches();
    for (final RuleEngine2.Rule r : engine.rules) {
      {
        if (!(matchAny("use helper table mech list *", r.comments, m)))
          continue;
      }
      List<String> entries = mL(m.unq(0));
      {
        if (empty(entries))
          continue;
      }
      final List<List<String>> helperTable = map("tok_splitAtCommaOrDoubleArrow", entries);
      final Map<String, Pair<Integer, Integer>> helperIndex = indexTwoDArrayIC(helperTable);
      r.addMapMassager(new gazelle_addHelperTablesToRules_Massager(helperTable, helperIndex));
    }
  }

  public static class gazelle_addVarSplittersToRules_VarSplitter {

    public String var;

    public boolean debug;

    public gazelle_addVarSplittersToRules_VarSplitter() {
    }

    public gazelle_addVarSplittersToRules_VarSplitter(String var, boolean debug) {
      this.debug = debug;
      this.var = var;
    }

    public Map<String, String> get(Map<String, String> map, List<String> tokC, List<String> tokI, RuleEngine2_MatchedRule matched) {
      if (map == null)
        return null;
      String value = getOrKeep(map, curly(var));
      List<String> tok1 = matched.tokenize(var);
      List<String> tok2 = matched.tokenize(uncurly(value));
      if (debug)
        print("VarSplitter: " + tok1 + " / " + tok2);
      if (l(tok1) != l(tok2))
        return null;
      int oldSize = l(map);
      for (int i = 1; i < l(tok1); i += 2) if (!strictPutIC(map, tok1.get(i), tok2.get(i)))
        return null;
      if (debug)
        print("VarSplitter done => " + map);
      matched.nPossibleVars = matched.nPossibleVars() + l(map) - oldSize;
      return map;
    }
  }

  public static void gazelle_addVarSplittersToRules(RuleEngine2 engine, Object... __) {
    Matches m = new Matches();
    boolean debug = boolPar("debug", __);
    for (final RuleEngine2.Rule r : engine.rules) {
      {
        if (!(jmatchAny("split *", r.comments, m) && isQuoted(m.get(0))))
          continue;
      }
      String var = m.unq(0);
      if (debug)
        print("Made splitter for: " + var);
      r.addMapMassager(new gazelle_addVarSplittersToRules_VarSplitter(var, debug));
    }
  }

  public static class gazelle_addVarDiscriminators_MechListContains extends F1<String, Boolean> {

    public String list;

    public gazelle_addVarDiscriminators_MechListContains() {
    }

    public gazelle_addVarDiscriminators_MechListContains(String list) {
      this.list = list;
    }

    public Boolean get(String s) {
      boolean ok = mechCISetContains(list, s);
      print("MechListContains: " + list + " / " + s + " => " + ok);
      return ok;
    }
  }

  public static class gazelle_addVarDiscriminators_VarDiscriminator {

    public String var;

    public Object discriminator;

    public boolean debug;

    public gazelle_addVarDiscriminators_VarDiscriminator() {
    }

    public gazelle_addVarDiscriminators_VarDiscriminator(String var, Object discriminator, boolean debug) {
      this.debug = debug;
      this.discriminator = discriminator;
      this.var = var;
    }

    public Map<String, String> get(Map<String, String> map, List<String> tokC, List<String> tokI, RuleEngine2_MatchedRule matched) {
      if (map == null)
        return null;
      String value = map.get(var);
      if (value != null && !isTrue(callAndMake_orF1(discriminator, value))) {
        if (debug)
          print("Rejecting var: " + var + " -> " + value + " because of " + discriminator);
        return null;
      }
      return map;
    }
  }

  public static void gazelle_addVarDiscriminators(RuleEngine2 engine, Object... __) {
    boolean debug = boolPar("debug", __) || boolPar("debugVarDiscriminators", __);
    for (final RuleEngine2.Rule r : engine.rules) {
      for (Matches m : getJMatches_all("var * should satisfy *", r.comments)) {
        String var = m.unq(0), discriminator = m.unq(1);
        if (debug)
          print("Made discriminator for: " + var);
        r.addMapMassager(new gazelle_addVarDiscriminators_VarDiscriminator(var, discriminator, debug));
      }
      for (Matches m : getJMatches_all("var * should be in list *", r.comments)) {
        String var = m.unq(0), list = m.unq(1);
        r.addMapMassager(new gazelle_addVarDiscriminators_VarDiscriminator(var, new gazelle_addVarDiscriminators_MechListContains(list), debug));
      }
      for (Matches m : getJMatches_all("* has to be between * and *", r.comments)) {
        String var = m.unq(0);
        double min = parseDouble(m.unq(1)), max = parseDouble(m.unq(2));
        if (debug)
          print("Made discriminator for: " + var);
        r.addMapMassager(new gazelle_addVarDiscriminators_VarDiscriminator(var, new F1<String, Boolean>() {

          public Boolean get(String value) {
            try {
              try {
                return isBetween_double(parseDouble(or(value, var)), min, max);
              } catch (Throwable __e) {
                silentException(__e);
              }
              return false;
            } catch (Exception __e) {
              throw rethrow(__e);
            }
          }

          public String toString() {
            return "pcall-silent {\r\n          ret isBetween_double(parseDouble(or(value, var)), m...";
          }
        }, debug));
      }
      for (Matches m : getJMatches_all("* has to be * or *", r.comments)) {
        String var = m.unq(0);
        Set<String> values = litciset(m.unq(1), m.unq(2));
        if (debug)
          print("Made discriminator for: " + var);
        r.addMapMassager(new gazelle_addVarDiscriminators_VarDiscriminator(var, new F1<String, Boolean>() {

          public Boolean get(String value) {
            try {
              return contains(values, or(value, var));
            } catch (Exception __e) {
              throw rethrow(__e);
            }
          }

          public String toString() {
            return "contains(values, or(value, var))";
          }
        }, debug));
      }
    }
  }

  public static class gazelle_addVarConvertersToRules_VarConverter {

    public String var1, var2;

    public Object converter;

    public boolean debug;

    public gazelle_addVarConvertersToRules_VarConverter() {
    }

    public gazelle_addVarConvertersToRules_VarConverter(String var1, String var2, Object converter, boolean debug) {
      this.debug = debug;
      this.converter = converter;
      this.var2 = var2;
      this.var1 = var1;
    }

    public Map<String, String> get(Map<String, String> map, List<String> tokC, List<String> tokI, RuleEngine2_MatchedRule matched) {
      if (map == null)
        return null;
      String value = map.get(var1);
      if (value == null)
        return map;
      String value2 = (String) callAndMake_orF1(converter, value);
      if (debug)
        print("VarConverter: " + value + " => " + value2);
      if (!strictPutIC(map, var2, value2))
        return null;
      return map;
    }
  }

  public static void gazelle_addVarConvertersToRules(RuleEngine2 engine, Object... __) {
    boolean debug = boolPar("debug", __) || boolPar("varConverterDebug", __);
    for (final RuleEngine2.Rule r : engine.rules) for (Matches m : getJMatches_all("convert var * to * with *", r.comments)) r.addMapMassager(new gazelle_addVarConvertersToRules_VarConverter(m.unq(0), m.unq(1), m.unq(2), debug));
  }

  public static class gazelle_addCalculationFunctionsToRules_Calculator {

    public String var1, var2, var3;

    public Object function;

    public boolean debug;

    public gazelle_addCalculationFunctionsToRules_Calculator() {
    }

    public gazelle_addCalculationFunctionsToRules_Calculator(String var1, String var2, String var3, Object function, boolean debug) {
      this.debug = debug;
      this.function = function;
      this.var3 = var3;
      this.var2 = var2;
      this.var1 = var1;
    }

    public Map<String, String> get(Map<String, String> map, List<String> tokC, List<String> tokI, RuleEngine2_MatchedRule matched) {
      if (map == null)
        return null;
      double result;
      try {
        String value1 = getOrKeep(map, var1), value2 = getOrKeep(map, var2);
        result = (double) callAndMake_orF2(function, parseDouble(value1), parseDouble(value2));
      } catch (Throwable _e) {
        return null;
      }
      if (!strictPutIC(map, var3, str(iround(result))))
        return null;
      return map;
    }
  }

  public static void gazelle_addCalculationFunctionsToRules(RuleEngine2 engine, Object... __) {
    boolean debug = boolPar("debug", __) || boolPar("calcDebug", __);
    for (final RuleEngine2.Rule r : engine.rules) for (Matches m : getJMatches_all("compute var * from * and * using *", r.comments)) {
      r.addMapMassager(new gazelle_addCalculationFunctionsToRules_Calculator(m.unq(1), m.unq(2), m.unq(0), m.unq(3), debug));
      if (debug)
        print("Calculator made");
    }
  }

  public static class gazelle_addRespondToHumanOnlyHelpers_Check {

    public boolean debug;

    public gazelle_addRespondToHumanOnlyHelpers_Check() {
    }

    public gazelle_addRespondToHumanOnlyHelpers_Check(boolean debug) {
      this.debug = debug;
    }

    public Map<String, String> get(Map<String, String> map, List<String> tokC, List<String> tokI, RuleEngine2_MatchedRule matched) {
      if (map == null)
        return null;
      if (debug)
        print("humanOnlyHelper for " + matched.ruleID() + ": " + matched.inputsByType);
      if (!eq("true", matched.inputsByType.get("_respondingToHuman")))
        return null;
      return map;
    }
  }

  public static void gazelle_addRespondToHumanOnlyHelpers(RuleEngine2 engine, Object... __) {
    boolean debug = boolPar("debug", __) || boolPar("humanOnlyDebug", __);
    for (final RuleEngine2.Rule r : engine.rules) if (cic(r.comments, "only answer to human")) {
      if (debug)
        print("Made human-only helper");
      r.addMapMassager(new gazelle_addRespondToHumanOnlyHelpers_Check(debug));
    }
  }

  public static class gazelle_addForbiddenBindingHelpers_Blocker {

    public String var, value;

    public boolean debug;

    public gazelle_addForbiddenBindingHelpers_Blocker() {
    }

    public gazelle_addForbiddenBindingHelpers_Blocker(String var, String value, boolean debug) {
      this.debug = debug;
      this.value = value;
      this.var = var;
    }

    public Map<String, String> get(Map<String, String> map, List<String> tokC, List<String> tokI, RuleEngine2_MatchedRule matched) {
      if (map == null)
        return null;
      if (value == null) {
        if (map.containsKey(var))
          return null;
      } else if (eqic(value, map.get(var)))
        return null;
      return map;
    }
  }

  public static class gazelle_addForbiddenBindingHelpers_TotalBlocker {

    public Map<String, String> get(Map<String, String> map, List<String> tokC, List<String> tokI, RuleEngine2_MatchedRule matched) {
      if (nempty(map))
        return null;
      return map;
    }
  }

  public static class gazelle_addForbiddenBindingHelpers_BlockAllExcept {

    public Set<String> allowedVars;

    public gazelle_addForbiddenBindingHelpers_BlockAllExcept() {
    }

    public gazelle_addForbiddenBindingHelpers_BlockAllExcept(Set<String> allowedVars) {
      this.allowedVars = allowedVars;
    }

    public Map<String, String> get(Map<String, String> map, List<String> tokC, List<String> tokI, RuleEngine2_MatchedRule matched) {
      if (containsKeysExcept(map, allowedVars))
        return null;
      return map;
    }
  }

  public static void gazelle_addForbiddenBindingHelpers(RuleEngine2 engine, Object... __) {
    boolean debug = boolPar("debug", __) || boolPar("forbiddenBindingsDebug", __);
    for (final RuleEngine2.Rule r : engine.rules) {
      for (Matches m : getJMatches_all("don't change * to *", r.comments)) r.addMapMassager(new gazelle_addForbiddenBindingHelpers_Blocker(m.unq(0), m.unq(1), debug));
      for (Matches m : getJMatches_all("don't change *", r.comments)) if (isQuoted(m.get(0)))
        r.addMapMassager(new gazelle_addForbiddenBindingHelpers_Blocker(m.unq(0), null, debug));
      if (cic(r.comments, "don't change anything"))
        r.addMapMassager(new gazelle_addForbiddenBindingHelpers_TotalBlocker());
      String var = matchAny_firstGroup("only var is *", r.comments);
      if (var != null)
        r.addMapMassager(new gazelle_addForbiddenBindingHelpers_BlockAllExcept(litciset(var)));
    }
  }

  public static Object[] paramsWithout(Object[] a1, Object... keys) {
    if (l(a1) == 1 && a1[0] instanceof Map)
      return new Object[] { mapMinus((Map) a1[0], keys) };
    Set set = lithashset(keys);
    List l = new ArrayList();
    for (int i = 0; i + 1 < l(a1); i += 2) if (!set.contains(a1[i])) {
      l.add(a1[i]);
      l.add(a1[i + 1]);
    }
    return toObjectArray(l);
  }

  public static List<GazelleTree> gazelle_getChildren(GazelleTree tree, Object... __) {
    if (tree == null)
      return null;
    if (tree.children != null)
      return tree.children;
    tree.children = new ArrayList();
    double qualityCutOff = optPar("qualityCutOff", __, -0.75);
    List<String> dialogHistory = (List<String>) (optPar("dialogHistory", __));
    boolean debug = boolPar("debug", __);
    if (tree.ctx == null)
      tree.setContext(assertNotNull("evalContext", (GazelleEvalContext) callF(assertNotNull("makeEvalContext", (Object) optPar("makeEvalContext", __)))));
    GazelleEvalContext ctx = tree.ctx;
    RuleEngine2 engine = ctx.engine;
    Map<String, String> inputMap = gazelle_inputMapFromLineAndHistory(tree.line, concatLists(dialogHistory, tree.history()));
    if (boolPar("respondingToHuman", __))
      inputMap.put("_respondingToHuman", "true");
    if (debug)
      print("inputMap=" + sfu(asTreeMap(inputMap)));
    if (!boolPar("skipSplitters", __))
      for (RuleEngine2.SimplifyWithRule rule : engine.splitterRules()) {
        try {
          Collection<String> out = gazelle_executeSplitterRule(engine, rule, tree.line);
          if (empty(out))
            continue;
          GazelleTree child = new GazelleTree(joinWithVBar(out));
          child.ctx = ctx;
          child.isSplitNode = true;
          tree.add(child);
          for (String line : out) child.add(new GazelleTree(line));
        } catch (Throwable __e) {
          _handleException(__e);
        }
      }
    boolean firstConditionOnly = boolPar("firstConditionOnly", __);
    Object[] params = litobjectarray("debug", optPar("debug", __), "tree", tree);
    List<RuleEngine2_MatchedRule> l = sortByMethodDesc("qualitySum", firstConditionOnly ? ai_ruleEngine2_rulesForInput_5(engine, inputMap, params) : ai_ruleEngine2_rulesForInput_5_allConditions(engine, inputMap, params));
    for (RuleEngine2_MatchedRule r : l) {
      double quality = r.relativeQuality();
      double cutOff = qualityCutOff;
      if (r.rule != null && r.rule.qualityCutOff != null)
        cutOff = r.rule.qualityCutOff;
      if (quality < cutOff && boolPar("skipBad", __, true)) {
        if (debug)
          print("Match under quality (" + quality + "/" + qualityCutOff + "): " + r);
        continue;
      }
      String line = r.outText();
      GazelleTree child = new GazelleTree(line);
      child.ctx = ctx;
      child.mr = r;
      if (r.rule != null)
        child.lineType = r.rule.outType();
      gazelle_addPredictionToTreeNode(child);
      tree.add(child);
    }
    gazelle_sortChildren(tree);
    return tree.children;
  }

  public static <A> TreeMap<String, A> ciMap() {
    return caseInsensitiveMap();
  }

  public static void gazelle_addPredictionToTreeNode(GazelleTree child, Object... __) {
    boolean debug = boolPar("debug", __);
    Set<String> badComments = (Set<String>) (optPar("badComments", __));
    Set<String> acceptablePurposes = (Set<String>) (optPar("acceptablePurposes", __));
    RuleEngine2_MatchedRule r = child.mr;
    GazelleEvalContext ctx = child.ctx;
    if (containsAny(r.rule.comments, badComments) || acceptablePurposes != null && !acceptablePurposes.contains(unnull(r.rule.purpose))) {
      if (debug)
        print("prediction=bad for various reasons");
      child.prediction = "bad";
    } else {
      if (ctx.predictor == null)
        child.prediction = null;
      else {
        child.prediction = ctx.predictor.get(ctx, r);
        if (debug)
          print("Prediction=" + child.prediction + " for " + r + " (" + (r.moreConditions() ? "unsatisfied" : "satisfied") + ") by " + ctx.predictor);
      }
    }
  }

  public static <A> List<A> sortByCalculatedFieldDesc(Collection<A> c, final Object f) {
    return sortByCalculatedFieldDesc_inPlace(cloneList(c), f);
  }

  public static <A> List<A> sortByCalculatedFieldDesc(Object f, Collection<A> c) {
    return sortByCalculatedFieldDesc(c, f);
  }

  public static int ai_goodBadToInt(String s) {
    if (eq(s, "good"))
      return 1;
    if (eq(s, "bad"))
      return -1;
    return 0;
  }

  public static String structureForUser(Object o) {
    return beautifyStructure(struct_noStringSharing(o));
  }

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

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

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

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

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

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

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

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

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

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

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

  public static boolean matchAny(Collection<String> patterns, String s, Matches m) {
    for (String pattern : patterns) if (match(pattern, s, m))
      return true;
    return false;
  }

  public static boolean matchAny(Collection<String> patterns, String s) {
    return matchAny(patterns, s, null);
  }

  public static boolean matchAny(String pat, Iterable<String> l) {
    return matchAny(pat, l, null);
  }

  public static boolean matchAny(String pat, Iterable<String> l, Matches m) {
    if (l != null)
      for (String s : l) if (match(pat, s, m))
        return true;
    return false;
  }

  public static List dm_gazelle_fulfillmentsForRule(String ruleID) {
    return (List) dm_call(dm_gazelle_fulfillmentsCRUD(), "entriesForRule", ruleID);
  }

  public static <B> TreeMap<String, B> asCIMap(Map<String, B> map) {
    return asCaseInsensitiveMap(map);
  }

  public static Map safeUnstructMap(String s) {
    return safeUnstructureMap(s);
  }

  public static String getString(Map map, Object key) {
    return map == null ? null : (String) map.get(key);
  }

  public static String getString(List l, int idx) {
    return (String) get(l, idx);
  }

  public static 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));
  }

  public static String getString(String key, Object o) {
    return getString(o, (Object) key);
  }

  public static List<String> identifiersOnly(Collection<String> l) {
    List<String> out = new ArrayList();
    for (String a : unnull(l)) if (isIdentifier(a))
      out.add(a);
    return out;
  }

  public static List<String> codeTokens(List<String> tok) {
    return codeTokensOnly(tok);
  }

  public static Map<String, String> mergeMapsStrictlyIC(Map<String, String> map1, Map<String, String> map2) {
    if (map2 == null)
      return map1;
    if (map1 == null)
      return map2;
    if (!canPutAllStrictlyIC(map1, map2))
      return null;
    return mergeMaps(map1, map2);
  }

  public static Map<String, String> ciMapWithoutKeysEqualToValues(Map<String, String> map) {
    if (map == null)
      return null;
    Map<String, String> map2 = ciMap();
    for (String key : keys(map)) {
      String value = map.get(key);
      if (!eqic(key, value))
        map2.put(key, value);
    }
    return map2;
  }

  public static boolean gazelle_isRecentlyUsedMapping(GazelleTree t, Map<String, String> varMap, Object... __) {
    String context = stringPar("context", __);
    if (empty(context))
      return false;
    return t != null && t.rule() != null && t.rule().avoidRecentlyUsedMappings && isTrue(dm_call(gazelle_rumCRUD(), "hasRecentlyUsedMapping", context, t.rule().globalID, varMap));
  }

  public static Map paramsToMap(Object... params) {
    int n = l(params);
    if (l(params) == 1 && params[0] instanceof Map)
      return (Map) params[0];
    LinkedHashMap map = new LinkedHashMap();
    for (int i = 0; i + 1 < n; i += 2) mapPut(map, params[i], params[i + 1]);
    return map;
  }

  public static boolean ai_ruleEngine2_matchStep(final RuleEngine2_MatchedRule matched, Map<String, String> inputsByType, boolean fillRemainingConditions, Object... __) {
    boolean debug = boolPar("debug", __);
    RuleEngine2.Rule rule = matched.rule;
    TextAndType in = rule.insWithType.get(matched.iCond);
    String input = inputsByType.get(in.type);
    if (input == null)
      return false;
    Object inputMassager = get(rule.inputMassagers, matched.iCond);
    Object checkerFunction = get(rule.checkerFunctions, matched.iCond);
    Object matcher = get(rule.matchers, matched.iCond);
    List<String> tokC = null, tokI = null;
    Map<String, String> map;
    if (inputMassager != null) {
      input = (String) callF(inputMassager, input);
      if (debug)
        print("Massaged input to: " + input);
      if (input == null)
        return false;
    }
    if (checkerFunction != null)
      map = (Map<String, String>) callF(checkerFunction, input);
    else {
      String cond = in.text;
      String tokenize = matched.tokenizationFunction;
      tokC = (List<String>) callAndMake(tokenize, cond);
      tokI = (List<String>) callAndMake(tokenize, input);
      if (debug)
        print("matchStep inputsByType=" + sfu(asTreeMap(inputsByType)));
      if (debug)
        print("matchStep in=" + in + ", iCond=" + matched.iCond + ", insWithType: " + rule.insWithType);
      if (debug)
        print("matchStep tokI/tokC: " + codeTokens(tokI) + " / " + codeTokens(tokC));
      if (contains(cond, "{}"))
        tokI = ai_applyWordSplitPattern1(tokC, tokI);
      if (matcher != null)
        map = (Map<String, String>) callF(matcher, tokC, tokI, matched);
      else
        map = zipCodeTokensToCIMap_strict_withoutEquals(tokC, tokI);
    }
    if (debug)
      print("matchStep for rule " + rule.globalID + " map: " + map);
    if (!gazelle_addMatch(matched, map, tokC, tokI, __)) {
      if (debug)
        print("addMatch failed - " + strWithIdentityHashCode(matched));
      return false;
    }
    matched.iCond++;
    if (debug)
      print("matchStep succeeded. " + matched);
    matched.matchedLines.add(pair(tokC, tokI));
    if (fillRemainingConditions) {
      matched.remainingConditions = nullIfEmpty(map(dropFirst(matched.iCond, rule.in), new F1<String, String>() {

        public String get(String s) {
          try {
            return matched.applyMappingTo(s);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "matched.applyMappingTo(s)";
        }
      }));
      matched.remainingConditionsWithType = nullIfEmpty(map(dropFirst(matched.iCond, rule.insWithType), new F1<TextAndType, TextAndType>() {

        public TextAndType get(TextAndType in) {
          try {
            return new TextAndType(matched.applyMappingTo(in.text), in.type);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "TextAndType(matched.applyMappingTo(in.text), in.type)";
        }
      }));
    }
    Map<Pair<String, String>, Integer> qualities = ai_ruleEngine2_mappingQualities(rule);
    for (String a : keys(map)) {
      String b = map.get(a);
      mapPut(matched.mappingQuality, a, qualities.get(pair(a, b)));
    }
    if (debug)
      print("iCond now=" + matched.iCond + ", next input: " + get(matched.rule.insWithType, matched.iCond));
    return true;
  }

  public static boolean gazelle_isEmbeddedEval(String s) {
    return startsWithEndsWith(s, "{=", "}");
  }

  public static <A> A requiredPar(ThreadLocal<A> tl) {
    return assertNotNull(optPar(tl));
  }

  public static Object requiredPar(Object[] params, String name) {
    Object o = optPar(params, name);
    if (o == null)
      throw fail("Need " + name);
    return o;
  }

  public static Object requiredPar(String name, Object[] params) {
    return requiredPar(params, name);
  }

  public static <B, A extends Collection<B>> A printIndentLines(String indent, A lines) {
    return printLinesWithIndent(indent, lines);
  }

  public static <B, A extends Collection<B>> A printIndentLines(A lines) {
    return printLinesWithIndent(defaultIndent(), lines);
  }

  public static <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;
  }

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

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

  public static <A, B extends A> List 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;
  }

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

  public static <A, B extends A> List 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;
  }

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

  public static List<String> matchAll_first(String pat, Iterable<String> l) {
    return map(matchAll(pat, l), new F1<Matches, String>() {

      public String get(Matches m) {
        try {
          return m.get(0);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "m.get(0)";
      }
    });
  }

  public static <A, B extends A> List<A> listMinusSet(Iterable<A> l, Collection<B> stuff) {
    if (empty(stuff))
      return asList(l);
    Set<B> set = asSet(stuff);
    List<A> l2 = new ArrayList();
    for (A a : l) if (!set.contains(a))
      l2.add(a);
    return l2;
  }

  public static <A, B extends A, C extends A> List<A> listMinusSet(Iterable<A> l, Collection<B> stuff, Collection<C> stuff2) {
    return listMinusSet(listMinusSet(l, stuff), stuff2);
  }

  public static List<GazelleTree> gazelle_reasonWithPreprocessing(String input1, Object... __) {
    F0<GazelleEvalContext> contextMaker = (F0<GazelleEvalContext>) (assertNotNull("Need contextMaker", optPar("contextMaker", __)));
    String userName = stringPar("userName", __);
    boolean debug = boolPar("debug", __);
    boolean debugPreprocessing = boolPar("debugPreprocessing", __);
    Collector<GInterpretable> interpretables_out = (Collector<GInterpretable>) (optPar("interpretables_out", __));
    Collection<GInterpretable> interpretables = gazelle_preprocess(input1, __);
    if (debugPreprocessing) {
      print("Got " + n2(interpretables, "interpretable") + ".");
      printIndentLines(interpretables);
    }
    if (interpretables != null)
      addAll(interpretables_out, interpretables);
    Set<String> acceptablePurposes = (Set<String>) (optPar("acceptablePurposes", __));
    if (acceptablePurposes == null)
      acceptablePurposes = litset("");
    List<GazelleTree> l2 = new ArrayList();
    Set<String> outputs = ciSet();
    for (GInterpretable intp : interpretables) {
      List<GazelleTree> results = dm_gazelle_reasonAboutChatInput_v2(userName, intp.text, paramsPlus(__, "acceptablePurposes", acceptablePurposes));
      print(intp.text + " => " + n2(results, "result"));
      for (GazelleTree tree : results) if (outputs.add(tree.line)) {
        if (tree.mr != null)
          tree.mr.interpretable = intp;
        l2.add(tree);
      }
    }
    gazelle_dropMatchedDefaultRules(l2, __);
    return l2;
  }

  public static <T> void sort(T[] a, Comparator<? super T> c) {
    Arrays.sort(a, c);
  }

  public static <T> void sort(T[] a) {
    Arrays.sort(a);
  }

  public static <T> void sort(List<T> a, Comparator<? super T> c) {
    Collections.sort(a, c);
  }

  public static void sort(List a) {
    Collections.sort(a);
  }

  public static int gazelle_historyLevelRequired(RuleEngine2.Rule rule) {
    if (rule == null)
      return 0;
    int n = 0;
    Matches m = new Matches();
    for (GRuleLine l : unnull(rule.insWithType)) if (eq(l.type, "standard"))
      n = max(n, 1);
    else if (startsWith(l.type, "history-", m))
      n = max(n, parseIntOpt(m.rest()));
    return n;
  }

  public static List<String> javaTokWithAllBrackets(String s) {
    return javaTokPlusBrackets2(s);
  }

  public static boolean eqGet(List l, int i, Object o) {
    return eq(get(l, i), o);
  }

  public static <A, B> boolean eqGet(Map<A, B> map, A key, Object o) {
    return eq(mapGet(map, key), o);
  }

  public static boolean containsNewLine(String s) {
    return contains(s, '\n');
  }

  public static List<String> splitAtTokenIndices(List<String> tok, List<Integer> indices) {
    List<String> l = new ArrayList();
    for (int iI = -1; iI < l(indices); iI++) {
      int i = or(get(indices, iI), 0) + 1;
      int j = or(get(indices, iI + 1), l(tok));
      l.add(trimJoin(tok.subList(i, j)));
    }
    return l;
  }

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

  public static List<String> mechList_tlft(String name) {
    return tlft(mechList_raw(name));
  }

  public static boolean isSnippetID(String s) {
    try {
      parseSnippetID(s);
      return true;
    } catch (RuntimeException e) {
      return false;
    }
  }

  public static 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);
      }
    }
    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;
  }

  public static Number parseIntOrLong(String s) {
    long l = parseLong(s);
    return longIsInt(l) ? (Number) (int) l : (Number) l;
  }

  public static boolean isLongConstant(String s) {
    if (!s.endsWith("L"))
      return false;
    s = s.substring(0, l(s) - 1);
    return isInteger(s);
  }

  public static long parseLong(String s) {
    if (s == null)
      return 0;
    return Long.parseLong(dropSuffix("L", s));
  }

  public static long parseLong(Object s) {
    return Long.parseLong((String) s);
  }

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

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

  public static RuntimeException quickFail(Throwable e) {
    throw asQuickException(e);
  }

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

  public static RuntimeException quickFail(String msg) {
    throw new QuickException(unnull(msg));
  }

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

  public static Map<Integer, Integer> getBracketMap(List tok) {
    return getBracketMap(tok, getBracketMap_opening, getBracketMap_closing);
  }

  public static Map<Integer, Integer> getBracketMap(List tok, Collection<String> opening, Collection<String> closing) {
    return getBracketMap(tok, opening, closing, 0, l(tok));
  }

  public static 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) {
      if (opening.contains(tok.get(i)))
        stack.add(i);
      else if (closing.contains(tok.get(i))) {
        if (!empty(stack))
          map.put(liftLast(stack), i);
      }
    }
    return map;
  }

  public static Set<String> getBracketMap_opening = lithashset("{", "(");

  public static Set<String> getBracketMap_closing = lithashset("}", ")");

  public static String trimJoinSubList(List<String> l, int i, int j) {
    return trim(join(subList(l, i, j)));
  }

  public static String trimJoinSubList(List<String> l, int i) {
    return trim(join(subList(l, i)));
  }

  public static void setCreatorTo(Object o, Object creator) {
    setOpt(o, "creator_class", newWeakReference(creator));
  }

  public static WeakReference<Object> creator_class;

  public static Object creator() {
    return creator_class == null ? null : creator_class.get();
  }

  public static <A> WeakReference<A> newWeakReference(A a) {
    return a == null ? null : new WeakReference(a);
  }

  public static Object callAndMake_orDirect(String functionName, List args) {
    return callAndMake_orDirect(functionName, toObjectArray(args));
  }

  public static Object callAndMake_orDirect(String functionName, Object... args) {
    if (functionName == null)
      return null;
    if (hasMethod(mc(), functionName, args))
      return callMC(functionName, args);
    else
      return makeAndCall_cached(functionName, args);
  }

  public static Map<String, Class> classForName_cache = synchroHashMap();

  public static Class classForName(String name) {
    try {
      Class c = classForName_cache.get(name);
      if (c == null)
        classForName_cache.put(name, c = Class.forName(name));
      return c;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static Map<Class, Constructor> nuObjectWithoutArguments_cache = newDangerousWeakHashMap();

  public static Object nuObjectWithoutArguments(String className) {
    try {
      return nuObjectWithoutArguments(classForName(className));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static <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);
    }
  }

  public static Constructor nuObjectWithoutArguments_findConstructor(Class c) {
    for (Constructor m : c.getDeclaredConstructors()) if (empty(m.getParameterTypes())) {
      m.setAccessible(true);
      return m;
    }
    throw fail("No default constructor found in " + c.getName());
  }

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

  public static 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);
  }

  public static <A, B> Set<A> keys(Map<A, B> map) {
    return map == null ? new HashSet() : map.keySet();
  }

  public static Set keys(Object map) {
    return keys((Map) map);
  }

  public static <A> Set<A> keys(MultiSet<A> ms) {
    return ms.keySet();
  }

  public static <A, B> Set<A> keys(MultiMap<A, B> mm) {
    return mm.keySet();
  }

  public static void set(Object o, String field, Object value) {
    if (o == null)
      return;
    if (o instanceof Class)
      set((Class) o, field, value);
    else
      try {
        Field f = set_findField(o.getClass(), field);
        f.setAccessible(true);
        smartSet(f, o, value);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
  }

  public static void set(Class c, String field, Object value) {
    if (c == null)
      return;
    try {
      Field f = set_findStaticField(c, field);
      f.setAccessible(true);
      smartSet(f, null, value);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public static 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());
  }

  public static 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());
  }

  public static void failIfOddCount(Object... list) {
    if (odd(l(list)))
      throw fail("Odd list size: " + list);
  }

  public static void saveGZStructureToFile(String file, Object o) {
    saveGZStructureToFile(getProgramFile(file), o);
  }

  public static void saveGZStructureToFile(File file, Object o) {
    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());
      try {
        GZIPOutputStream gos = new GZIPOutputStream(fileOutputStream);
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(gos, "UTF-8");
        PrintWriter printWriter = new PrintWriter(outputStreamWriter);
        structureToPrintWriter(o, printWriter);
        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);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static void printFileSaved(File f) {
    print("Saved " + fileInfo(f));
  }

  public static File gazelle_discord_reasoningFileForContext(String context) {
    return empty(context) ? null : newFile(gazelle_discordReasoningsDir(), uniqueFileNameUsingMD5_80_v2(context) + ".gz");
  }

  public static List<Object> gazelle_applicationsForContext(String context) {
    return (List) dm_call(dm_gazelle_longFeedbackCRUD(), "applicationsForContext", context);
  }

  public static String gazelle_matchedRuleStructForContext(String context) {
    return gazelle_matchedRuleStructFromReasoningForLine(unstructGZFile(gazelle_discord_reasoningFileForContext(context)));
  }

  public static void dm_gazelle_saveAppliedRuleToLongList(Object... params) {
    dm_requireAndCall("#1021787/AppliedRules", "uniqConcept", (Object) params);
  }

  public static List<Object> dm_gazelle_applicationsForMsgID(long msgID) {
    return (List) dm_call(dm_gazelle_longFeedbackCRUD(), "applicationsForContext", "discord msg " + msgID);
  }

  public static String stringOptPar(Object[] params, String name) {
    return (String) optPar(params, name);
  }

  public static boolean eqOneOf(Object o, Object... l) {
    for (Object x : l) if (eq(o, x))
      return true;
    return false;
  }

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

  public static Object mainBot;

  public static Object getMainBot() {
    return mainBot;
  }

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

  public static <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);
    }
  }

  public static boolean even(int i) {
    return (i & 1) == 0;
  }

  public static boolean even(long i) {
    return (i & 1) == 0;
  }

  public static <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;
  }

  public static Object[] mapToObjectArray(Map map) {
    List l = new ArrayList();
    for (Object o : keys(map)) {
      l.add(o);
      l.add(map.get(o));
    }
    return toObjectArray(l);
  }

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

  public static String joinWithSpace(Collection<String> c) {
    return join(" ", c);
  }

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

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

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

  public static void arraycopy(Object[] a, Object[] b) {
    int n = min(a.length, b.length);
    for (int i = 0; i < n; i++) b[i] = a[i];
  }

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

  public static <A> A[] arrayOfType(Class<A> type, int n) {
    return makeArray(type, n);
  }

  public static <A> A[] arrayOfType(int n, Class<A> type) {
    return arrayOfType(type, n);
  }

  public static 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;
  }

  public static List<String> splitAtTokens(String s, List<String> tokens) {
    return splitAtTokens(javaTok(s), tokens);
  }

  public static List<String> splitAtTokens(List<String> tok, List<String> tokens) {
    List<String> l = new ArrayList();
    int i = 0;
    while (i < l(tok)) {
      int j = indexOfSubList(tok, tokens, i);
      if (i >= l(tok))
        break;
      if (j < 0)
        j = l(tok);
      l.add(trimJoin(tok.subList(i, j)));
      i = j + l(tokens);
    }
    return l;
  }

  public static List<String> splitAtTokens(List<String> tok, String... tokens) {
    List<String> l = new ArrayList();
    int i = 0;
    while (i < l(tok)) {
      int j = indexOfSubList(tok, tokens, i);
      if (i >= l(tok))
        break;
      if (j < 0)
        j = l(tok);
      l.add(trimJoin(tok.subList(i, j)));
      i = j + l(tokens);
    }
    return l;
  }

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

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

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

  public static 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;
  }

  public static String replacePrefix(String prefix, String replacement, String s) {
    if (!startsWith(s, prefix))
      return s;
    return replacement + substring(s, l(prefix));
  }

  public static Throwable innerException2(Throwable e) {
    if (e == null)
      return null;
    while (empty(e.getMessage()) && e.getCause() != null) e = e.getCause();
    return e;
  }

  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;
  }

  public static 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;
  }

  public static betterCIComparator_C betterCIComparator_instance;

  public static betterCIComparator_C betterCIComparator() {
    if (betterCIComparator_instance == null)
      betterCIComparator_instance = new betterCIComparator_C();
    return betterCIComparator_instance;
  }

  public static 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;
    }
  }

  public static boolean isCISet(Iterable<String> l) {
    return l instanceof TreeSet && ((TreeSet) l).comparator() == caseInsensitiveComparator();
  }

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

  public static Object pcallFAll_returnFirstNotNull(Iterator it, Object... args) {
    while (it.hasNext()) {
      Object __908 = pcallF(it.next(), args);
      if (__908 != null)
        return __908;
    }
    return null;
  }

  public static File programDir_mine;

  public static File programDir() {
    return programDir(getProgramID());
  }

  public static 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;
  }

  public static File programDir(String snippetID, String subPath) {
    return new File(programDir(snippetID), subPath);
  }

  public static File prepareProgramFile(String name) {
    return mkdirsForFile(getProgramFile(name));
  }

  public static File prepareProgramFile(String progID, String name) {
    return mkdirsForFile(getProgramFile(progID, name));
  }

  public static TimeZone localTimeZone() {
    return getTimeZone(standardTimeZone());
  }

  public static MechMode mechMode_value = new MechMode();

  public static MechMode mechMode() {
    return mechMode_value;
  }

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

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

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

  public static 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;
  }

  public static String assertIdentifier(String s) {
    return assertIsIdentifier(s);
  }

  public static Object dm_os() {
    {
      Object __1563 = vm_generalMap_get("stefansOS");
      if (__1563 != null)
        return __1563;
    }
    return creator();
  }

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

  public static Object swingAndWait(final Object f) {
    if (isAWTThread())
      return callF(f);
    else {
      final 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();
    }
  }

  public static String structOrNull(Object o) {
    return o == null ? null : struct(o);
  }

  public static Object unstructureGZFile(File f) {
    try {
      BufferedReader reader = utf8BufferedReader(gzInputStream(f));
      return unstructure_tok(javaTokC_noMLS_onReader(reader), false, null);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static File gazelle_discordReasoningsDir() {
    return javaxDataDir("Discord Reasonings");
  }

  public static 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);
  }

  public static <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;
  }

  public static <A> List<A> repeat(int n, A a) {
    return repeat(a, n);
  }

  public static int indent_default = 2;

  public static String indent(int indent) {
    return repeat(' ', indent);
  }

  public static String indent(int indent, String s) {
    return indent(repeat(' ', indent), s);
  }

  public static String indent(String indent, String s) {
    return indent + s.replace("\n", "\n" + indent);
  }

  public static String indent(String s) {
    return indent(indent_default, s);
  }

  public static 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;
  }

  public static void removeFromMultiPort(long vport) {
    if (vport == 0)
      return;
    for (Object port : getMultiPorts()) call(port, "removePort", vport);
  }

  public static 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;
  }

  public static 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);
  }

  public static String callStaticAnswerMethod(String s) {
    return callStaticAnswerMethod(mc(), s);
  }

  public static String callStaticAnswerMethod(String s, List<String> history) {
    return callStaticAnswerMethod(mc(), s, history);
  }

  public static 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);
  }

  public static List<Object> record_list = synchroList();

  public static void record(Object o) {
    record_list.add(o);
  }

  public static Object addToMultiPort_responder;

  public static 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;
      }
    });
  }

  public static 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);
  }

  public static AtomicInteger dialogServer_clients = new AtomicInteger();

  public static boolean dialogServer_printConnects;

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

  public static Set<String> dialogServer_knownClients = synchroTreeSet();

  public static int startDialogServerOnPortAbove(int port, DialogHandler handler) {
    while (!forbiddenPort(port) && !startDialogServerIfPortAvailable(port, handler)) ++port;
    return port;
  }

  public static int startDialogServerOnPortAboveDaemon(int port, DialogHandler handler) {
    while (!forbiddenPort(port) && !startDialogServerIfPortAvailable(port, handler, true)) ++port;
    return port;
  }

  public static void startDialogServer(int port, DialogHandler handler) {
    if (!startDialogServerIfPortAvailable(port, handler))
      throw fail("Can't start dialog server on port " + port);
  }

  public static boolean startDialogServerIfPortAvailable(int port, final DialogHandler handler) {
    return startDialogServerIfPortAvailable(port, handler, false);
  }

  public static ServerSocket startDialogServer_serverSocket;

  public static boolean startDialogServerIfPortAvailable(int port, final DialogHandler 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;
  }

  public static Thread startThread(Object runnable) {
    return startThread(defaultThreadName(), runnable);
  }

  public static Thread startThread(String name, Object runnable) {
    runnable = wrapAsActivity(runnable);
    return startThread(newThread(toRunnable(runnable), name));
  }

  public static Thread startThread(Thread t) {
    _registerThread(t);
    t.start();
    return t;
  }

  public static volatile boolean readLine_noReadLine;

  public static String readLine_lastInput;

  public static String readLine_prefix = "[] ";

  public static String readLine() {
    if (readLine_noReadLine)
      return null;
    String s = readLineHidden();
    if (s != null) {
      readLine_lastInput = s;
      print(readLine_prefix + s);
    }
    return s;
  }

  public static String getInnerMessage(Throwable e) {
    if (e == null)
      return null;
    return getInnerException(e).getMessage();
  }

  public static boolean publicCommOn() {
    return "1".equals(loadTextFile(new File(userHome(), ".javax/public-communication")));
  }

  public static int randomID_defaultLength = 12;

  public static String randomID(int length) {
    return makeRandomID(length);
  }

  public static String randomID() {
    return randomID(randomID_defaultLength);
  }

  public static boolean match3(String pat, String s) {
    return match3(pat, s, null);
  }

  public static boolean match3(String pat, String s, Matches matches) {
    if (pat == null || s == null)
      return false;
    return match3(pat, parse3_cachedInput(s), matches);
  }

  public static boolean match3(String pat, List<String> toks, Matches matches) {
    List<String> tokpat = parse3_cachedPattern(pat);
    return match3(tokpat, toks, matches);
  }

  public static 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;
  }

  public static Throwable printStackTrace(Throwable e) {
    print(getStackTrace(e));
    return e;
  }

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

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

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

  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;
  }

  public static String processID_cached;

  public static String getPID() {
    if (processID_cached == null) {
      String name = ManagementFactory.getRuntimeMXBean().getName();
      processID_cached = name.replaceAll("@.*", "");
    }
    return processID_cached;
  }

  public static String getInjectionID() {
    return (String) call(getJavaX(), "getInjectionID", getMainClass());
  }

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

  public static String getProgramTitle() {
    return getProgramName();
  }

  public static 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;
  }

  public static 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);
      }
    };
  }

  public static String defaultBotName() {
    return getProgramTitle() + ".";
  }

  public static Object dm_getStemByID(Object id) {
    return dm_callOS("getModuleByID", str(id));
  }

  public static DynModule dm_currentModuleMandatory() {
    return dm_current_mandatory();
  }

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

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

      public String toString() {
        return "l.remove(a);";
      }

      public void close() throws Exception {
        l.remove(a);
      }
    };
  }

  public static <A, B, C, D> List<T3<A, B, C>> collectThreeFieldsAsTriple(final String fieldA, final String fieldB, final String fieldC, Iterable<D> l) {
    return map(l, new F1<C, T3<A, B, C>>() {

      public T3<A, B, C> get(C c) {
        try {
          return t3((A) getOpt(c, fieldA), (B) getOpt(c, fieldB), (C) getOpt(c, fieldC));
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "t3((A) getOpt(c, fieldA), (B) getOpt(c, fieldB), (C) getOpt(c, fieldC))";
      }
    });
  }

  public static List<String> tok_combineRoundOrCurlyBrackets_keep(List<String> tok) {
    List<String> l = new ArrayList();
    for (int i = 0; i < l(tok); i++) {
      String t = tok.get(i);
      if (odd(i) && eqOneOf(t, "{", "(")) {
        int j = findEndOfBracketPart(tok, i);
        l.add(joinSubList(tok, i, j));
        i = j - 1;
      } else
        l.add(t);
    }
    return l;
  }

  public static List<String> javaTokPlusBrackets2(String s) {
    return tok_combineRoundCurlySquareBrackets_keep(javaTok(s));
  }

  public static boolean matchEndX(String pat, String s, Matches matches) {
    return matchEnd(dropPrefix("...", pat), s, matches);
  }

  public static Object dm_callModule(Object moduleOrID, String method, Object... args) {
    Object mod = dm_getModule(moduleOrID);
    if (mod == null)
      return null;
    AutoCloseable __1347 = dm_enter(mod);
    try {
      return call(mod, method, args);
    } finally {
      _close(__1347);
    }
  }

  public static <A, B> Map<A, B> syncMRUCache(int size) {
    return synchroMap(new MRUCache(size));
  }

  public static boolean gazelle_isEvalCmd(String s) {
    return swicOneOf(trim(s), "!eval", "!fresh", "!real-eval");
  }

  public static Map<String, Pair<String, Map<String, String>>> mechHashMap_cache = synchroMap();

  public static Map<String, String> mechHashMap(String listName) {
    Pair<String, Map<String, String>> p = mechHashMap_cache.get(listName);
    String text = mechList_raw(listName);
    if (p != null && eq(p.a, text))
      return p.b;
    String parsing = first(mechList_opt(listName + " | Parsing"));
    p = pair(text, (Map<String, String>) (parsing != null ? asHashMap((Map<String, String>) callAndMake(parsing, text)) : parseDoubleArrowMap(new HashMap(), tlft(text))));
    mechHashMap_cache.put(listName, p);
    return p.b;
  }

  public static 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;
  }

  public static <A> List<A> uniquifyList(Collection<A> l) {
    if (l == null)
      return null;
    if (l(l) < 2)
      return asList(l);
    HashSet<A> set = new HashSet();
    List<A> out = new ArrayList();
    for (A a : l) if (set.add(a))
      out.add(a);
    return out;
  }

  public static <A> boolean containsAnythingNeq(Collection<A> l, A a) {
    for (A x : unnull(l)) if (neq(x, a))
      return true;
    return false;
  }

  public static <A> List<A> listMinus(Collection<A> l, Object... stuff) {
    if (l == null)
      return null;
    List l2 = similarEmptyList(l);
    Set set = lithashset(stuff);
    for (Object o : l) if (!set.contains(o))
      l2.add(o);
    return l2;
  }

  public static List dm_modulesOfType(String type) {
    List<String> l = splitAtSlash(type);
    if (l(l) == 2)
      return filterByProgramIDAndShortClassName(dm_listModules(), first(l), second(l));
    else
      return filterByShortClassName(dm_listModules(), type);
  }

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

  public static <A, B> A eitherAOpt(Either<A, B> e) {
    return e != null && e.isA() ? e.a() : null;
  }

  public static Set<Thread> evalWithTimeout_inTime = synchroSet();

  public static Set<Thread> evalWithTimeout_allThreads = newWeakHashSet();

  public static ThreadLocal<String> evalWithTimeout_threadName = new ThreadLocal();

  public static Either<Object, Thread> evalWithTimeout(int timeoutMS, final Object r) {
    final Flag done = new Flag();
    final Flag doneWaiting = new Flag();
    final Var var = new Var();
    final Var<Throwable> error = new Var();
    Thread t = newThread(getAndClearThreadLocal(evalWithTimeout_threadName), new Runnable() {

      public void run() {
        try {
          try {
            try {
              var.set(callF(r));
            } finally {
              evalWithTimeout_allThreads.remove(currentThread());
            }
          } catch (Throwable e) {
            error.set(e);
            if (doneWaiting.isUp())
              printStackTrace(e);
          } finally {
            done.raise();
          }
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "try {\r\n      try {\r\n        var.set(callF(r));\r\n      } finally {\r\n        ev...";
      }
    });
    beforeDelegatingToThread(t);
    try {
      startThread(t);
      evalWithTimeout_inTime.add(t);
      evalWithTimeout_allThreads.add(t);
      try {
        done.waitUntilUp(timeoutMS);
        doneWaiting.raise();
      } finally {
        evalWithTimeout_inTime.remove(t);
      }
      if (!done.isUp()) {
        print("Cancelling thread (timeout)");
        cancelAndInterruptThread(t);
        return either2(t);
      }
      if (error.get() != null)
        rethrow(error.get());
      return either1(var.get());
    } finally {
      afterDelegatingToThread(t);
    }
  }

  public static Either<Object, Thread> evalWithTimeout(double timeoutSeconds, final Object r) {
    return evalWithTimeout(toMS_int(timeoutSeconds), r);
  }

  public static String evalJava_prep(String code) {
    return evalJava_prep(code, "calc");
  }

  public static String evalJava_prep(String code, String mainName) {
    return evalJava_prep(code, mainName, "");
  }

  public static ThreadLocal<String> evalJava_prep_args = new ThreadLocal();

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

  public static String evalJava_prep(String code, String mainName, String preCode) {
    List<String> tok = javaTok(trim(code));
    if (!isFalse(evalJava_prep_voidMagic.get()) && tok_shouldAddReturn(tok) && eqGet(tok, 3, "(") && isIdentifier(get(tok, 1)) && isKnownVoidFunction(get(tok, 1)))
      tokAppend_reTok(tok, l(tok) - 2, ";");
    code = tok_addReturn(tok);
    String returnType = containsReturnWithArgument(code) ? "O" : "void";
    String main = "static " + returnType + " " + mainName + "(" + unnull(evalJava_prep_args.get()) + ") throws Exception { " + preCode + code + "\n" + "}";
    return main;
  }

  public static Class veryQuickJava(CharSequence mainJava) {
    return veryQuickJava3(str(mainJava));
  }

  public static Object makeDependent_postProcess;

  public static 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);
  }

  public static ThreadLocal<Long> evalJava_myProgramID_main_time = new ThreadLocal();

  public static Object evalJava_myProgramID_main(String main, Object... args) {
    Object obj = veryQuickJava(main);
    setOpt(obj, "programID", programID());
    makeDependent(obj);
    return evalJava_myProgramID_main_2(obj, args);
  }

  public static Object evalJava_myProgramID_main_2(Object obj, Object... args) {
    long time = sysNow();
    try {
      return callCalc(obj, args);
    } finally {
      evalJava_myProgramID_main_time.set(sysNow() - time);
    }
  }

  public static int indexOfIgnoreCase_manual(String a, String b) {
    int la = l(a), lb = l(b);
    if (la < lb)
      return -1;
    int n = la - lb;
    loop: for (int i = 0; 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 List<String> tok_combineCurlyBrackets_keep(List<String> tok) {
    List<String> l = new ArrayList();
    for (int i = 0; i < l(tok); i++) {
      String t = tok.get(i);
      if (odd(i) && eq(t, "{")) {
        int j = findEndOfCurlyBracketPart(tok, i);
        l.add(joinSubList(tok, i, j));
        i = j - 1;
      } else
        l.add(t);
    }
    return l;
  }

  public static boolean startsWithAndEndsWith(String s, String prefix, String suffix) {
    return startsWith(s, prefix) && endsWith(s, suffix);
  }

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

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

  public static File remoteMechListMirrorMetaFile(String listName) {
    return javaxDataDir("Remote Mech Lists/" + uniqueFileNameUsingMD5_80_v2(upper(listName)) + ".meta");
  }

  public static Map<String, List<String>> parse3_cachedInput_cache = synchronizedMRUCache(1000);

  public static 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;
  }

  public static Map<String, List<String>> parse3_cachedPattern_cache = synchronizedMRUCache(1000);

  public static synchronized 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;
  }

  public static 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);
  }

  public static 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()]);
  }

  public static <A, B> Map<A, B> synchronizedMRUCache(int maxSize) {
    return synchroMap(new MRUCache(maxSize));
  }

  public static 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;
  }

  public static boolean isMultilineQuoted(String s) {
    if (!startsWith(s, "["))
      return false;
    int i = 1;
    while (i < s.length() && s.charAt(i) == '=') ++i;
    return i < s.length() && s.charAt(i) == '[';
  }

  public static <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));
  }

  public static IterableIterator<String> toLines(File f) {
    return linesFromFile(f);
  }

  public static 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;
  }

  public static int toLines_nextLineBreak(String s, int start) {
    for (int i = start; i < s.length(); i++) {
      char c = s.charAt(i);
      if (c == '\r' || c == '\n')
        return i;
    }
    return -1;
  }

  public static CloseableIterableIterator<String> linesFromFile(File f) {
    try {
      if (!f.exists())
        return emptyCloseableIterableIterator();
      if (ewic(f.getName(), ".gz"))
        return linesFromReader(utf8bufferedReader(newGZIPInputStream(f)));
      return linesFromReader(utf8bufferedReader(f));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static List<String> mL(String name) {
    return mechList_opt(name);
  }

  public static <A> A[] newObjectArrayOfSameType(A[] a, int n) {
    return (A[]) Array.newInstance(a.getClass().getComponentType(), n);
  }

  public static 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;
  }

  public static Class getClass_vmName(Object realm, String name) {
    try {
      try {
        return getClass(realm).getClassLoader().loadClass(name);
      } catch (ClassNotFoundException e) {
        return null;
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static <A> A printHidingCredentials(A o) {
    print(hideCredentials(str(o)));
    return o;
  }

  public static void logQuotedWithDate(String s) {
    logQuotedWithTime(s);
  }

  public static void logQuotedWithDate(String logFile, String s) {
    logQuotedWithTime(logFile, s);
  }

  public static void logQuotedWithDate(File logFile, String s) {
    logQuotedWithTime(logFile, s);
  }

  public static File infoBoxesLogFile() {
    return new File(javaxDataDir(), "Logs/infoBoxes.txt");
  }

  public static JWindow showWindow(Component c) {
    JWindow w = new JWindow();
    w.add(wrap(c));
    return w;
  }

  public static JPanel infoMessage_makePanel(String text) {
    final JTextArea ta = wrappedTextArea(text);
    onClick(ta, new Runnable() {

      public void run() {
        try {
          disposeWindow(ta);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "disposeWindow(ta)";
      }
    });
    int size = 14;
    if (l(text) <= 50)
      size *= 2;
    else if (l(text) < 100)
      size = iround(size * 1.5);
    ta.setFont(typeWriterFont(size));
    JScrollPane sp = jscroll(ta);
    return withMargin(sp);
  }

  public static int moveToTopRightCorner_inset = 20;

  public static <A extends Component> A moveToTopRightCorner(A a) {
    return moveToTopRightCorner(moveToTopRightCorner_inset, moveToTopRightCorner_inset, a);
  }

  public static <A extends Component> A moveToTopRightCorner(int insetX, int insetY, A a) {
    Window w = getWindow(a);
    if (w != null)
      w.setLocation(getScreenSize().width - w.getWidth() - insetX, insetY);
    return a;
  }

  public static <A extends Window> A disposeWindowAfter(int delay, final A w) {
    if (w != null)
      swingLater(delay, new Runnable() {

        public void run() {
          try {
            w.dispose();
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "w.dispose();";
        }
      });
    return w;
  }

  public static <A extends Window> A disposeWindowAfter(A w, double seconds) {
    return disposeWindowAfter(toMS_int(seconds), w);
  }

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

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

  public static Map<Class, Constructor> nuEmptyObject_cache = newDangerousWeakHashMap();

  public static <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));
          ctr.setAccessible(true);
        }
      }
      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);
    }
  }

  public static Constructor nuEmptyObject_findConstructor(Class c) {
    for (Constructor m : c.getDeclaredConstructors()) if (m.getParameterTypes().length == 0)
      return m;
    throw fail("No default constructor declared in " + c.getName());
  }

  public static <A> A copyFields(Object x, A y, String... fields) {
    if (empty(fields)) {
      Map<String, Object> map = objectToMap(x);
      for (String field : map.keySet()) setOpt(y, field, map.get(field));
    } else
      for (String field : fields) {
        Object o = getOpt(x, field);
        if (o != null)
          setOpt(y, field, o);
      }
    return y;
  }

  public static <A> A copyFields(Object x, A y, Collection<String> fields) {
    return copyFields(x, y, asStringArray(fields));
  }

  public static 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);
    }
  }

  public static Map<Integer, Integer> curlyBracketMap(List<String> tok) {
    TreeMap<Integer, Integer> map = new TreeMap();
    List<Integer> stack = new ArrayList();
    int n = l(tok);
    for (int i = 1; i < n; i += 2) {
      String t = tok.get(i);
      if (eq(t, "{"))
        stack.add(i);
      else if (eq(t, "}") && nempty(stack))
        map.put(liftLast(stack), i);
    }
    return map;
  }

  public static boolean isSafeCodeFragment(String code) {
    return eq("safe", joinWithComma(getCodeFragmentSafety(code)));
  }

  public static void replaceTokens(List<String> tok, int i, int j, String s) {
    clearAllTokens(subList(tok, i, j));
    tok.set(i, s);
  }

  public static void replaceTokens(List<String> tok, String s) {
    clearAllTokens(tok);
    tok.set(0, s);
  }

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

  public static <A, B> void mapPut(Map<A, B> map, A key, B value) {
    if (map != null && key != null && value != null)
      map.put(key, value);
  }

  public static String formatWithThousandsSeparator(long l) {
    return NumberFormat.getInstance(new Locale("en_US")).format(l);
  }

  public static void gazelle_changeInTypesForRulesWithMultipleInputs(RuleEngine2 e) {
    for (RuleEngine2.Rule r : e.rules) {
      int i = 0;
      while (i < l(r.insWithType) && eq(r.insWithType.get(i).type, "standard")) ++i;
      if (i > 1) {
        reverseInPlace(subList(r.insWithType, 0, i));
        for (int j = 1; j < i; j++) r.insWithType.get(j).type = "history-" + (j + 1);
      }
    }
  }

  public static class dm_gazelle_statementsFromAppliedRules_ByRule {

    public String ruleID;

    public MultiSet<String> statements = new MultiSet();
  }

  public static MultiSet<String> dm_gazelle_statementsFromAppliedRules_multiSet() {
    List concepts = cloneList((List) dm_requireAndCall("#1021413/AppliedRules", "concepts"));
    Map<String, dm_gazelle_statementsFromAppliedRules_ByRule> ruleMap = new HashMap();
    for (Object c : concepts) {
      try {
        String judgement = getString("judgement", c);
        String struct = getString("matchedRuleStruct", c);
        RuleEngine2_MatchedRule mr = (RuleEngine2_MatchedRule) (unstructureUnlessContainsAnonymousClasses(struct));
        if (mr == null)
          continue;
        String ruleID = mr.rule.globalID;
        dm_gazelle_statementsFromAppliedRules_ByRule r = getOrCreate_f1(ruleMap, ruleID, new F1<String, dm_gazelle_statementsFromAppliedRules_ByRule>() {

          public dm_gazelle_statementsFromAppliedRules_ByRule get(String ruleID) {
            try {
              return nu(dm_gazelle_statementsFromAppliedRules_ByRule.class, "ruleID", ruleID);
            } catch (Exception __e) {
              throw rethrow(__e);
            }
          }

          public String toString() {
            return "nu(dm_gazelle_statementsFromAppliedRules_ByRule, +ruleID)";
          }
        });
        r.statements.addAll(ai_gazelle_statementsFromAppliedRule(mr, judgement));
      } catch (Throwable __e) {
        _handleException(__e);
      }
    }
    MultiSet<String> out = new MultiSet();
    for (dm_gazelle_statementsFromAppliedRules_ByRule r : values(ruleMap)) for (String s : keys(r.statements)) out.add("Rule " + r.ruleID + ": " + s, r.statements.get(s));
    return out;
  }

  public static Map<String, MultiSet<String>> ai_gazelle_indexStatementsByRule_multiSet(MultiSet<String> statements) {
    Map<String, MultiSet<String>> map = new HashMap();
    Matches m = new Matches();
    for (String s : keys(statements)) if (matchStartX("Rule *: ...", s, m)) {
      String ruleID = m.unq(0);
      getOrCreateMultiSet(map, ruleID).add(m.rest(), statements.get(s));
    }
    return map;
  }

  public static ai_gazelle_analyzeStatementsForRule_Data ai_gazelle_analyzeStatementsForRule_multiSet(MultiSet<String> l) {
    ai_gazelle_analyzeStatementsForRule_Data data = ai_gazelle_analyzeStatementsForRule(keys(l));
    data.statements = l;
    data.varScores = new HashMap();
    Matches m = new Matches();
    for (String s : keys(l)) if (match("in a * mapping, variable * was mapped to *", s, m)) {
      Pair<String, String> p = pair(m.unq(1), m.unq(2));
      PosNegScore score = getOrCreatePosNegScore(data.varScores, p);
      if (eq(m.unq(0), "good"))
        score.pos += l.get(s);
      else if (eq(m.unq(0), "bad"))
        score.neg += l.get(s);
    }
    return data;
  }

  public static GazellePredictor dm_gazelle_standardPredictor(Object... __) {
    return (ctx, r) -> {
      ai_gazelle_analyzeStatementsForRule_Data data = ctx.dataByRule.get(r.rule.globalID);
      return data == null ? null : ai_gazelle_predictor1_onData(r, data, __);
    };
  }

  public static int done2_minPrint = 10;

  public static long done2(long startTime, String desc) {
    return done2(startTime, desc, done2_minPrint);
  }

  public static long done2(long startTime, String desc, int minPrint) {
    long time = sysNow() - startTime;
    saveTiming_noPrint(time);
    if (time >= minPrint)
      print(desc + " [" + time + " ms]");
    return time;
  }

  public static long done2(String desc, long startTime) {
    return done2(startTime, desc);
  }

  public static long done2(long startTime) {
    return done2(startTime, "");
  }

  public static <A> Object dm_codeWithHintsToFunctionOnString(String code) {
    return dm_codeWithHintsToFunctionOnString(code, "");
  }

  public static <A> Object dm_codeWithHintsToFunctionOnString(String code, String hints) {
    return dm_requireAndCallModule("#1021247/FunctionsOnStrings", "getFunction", code, hints);
  }

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

  public static int indexOfIC(List<String> a, String b) {
    return indexOfIgnoreCase(a, b);
  }

  public static int indexOfIC(List<String> a, String b, int i) {
    return indexOfIgnoreCase(a, b, i);
  }

  public static int indexOfIC(String a, String b) {
    return indexOfIgnoreCase(a, b);
  }

  public static List<String> tok_splitAtCommaOrDoubleArrow(String s) {
    return tok_splitAtCommaOrDoubleArrow(javaTok(s));
  }

  public static List<String> tok_splitAtCommaOrDoubleArrow(List<String> tok) {
    List<String> l = splitAtDoubleArrow(tok);
    if (l(l) > 1)
      return l;
    return tok_splitAtComma(tok);
  }

  public static Map<String, Pair<Integer, Integer>> indexTwoDArrayIC(List<List<String>> table) {
    if (table == null)
      return null;
    Map<String, Pair<Integer, Integer>> map = ciMap();
    for (int i = 0; i < l(table); i++) {
      List<String> row = table.get(i);
      for (int j = 0; j < l(row); j++) map.put(row.get(j), pair(i, j));
    }
    return map;
  }

  public static <A> A getOrKeep(Map<A, ? extends A> map, A a) {
    if (map == null)
      return a;
    A v = map.get(a);
    return v != null ? v : a;
  }

  public static String curly(String s) {
    return optionalCurlyBrace(s);
  }

  public static String uncurly(String s) {
    return tok_unCurlyBracket(s);
  }

  public static <A> boolean strictPutIC(Map<A, String> map, A key, String value) {
    if (!(map != null && key != null && value != null))
      return true;
    String old = map.get(key);
    if (old == null) {
      map.put(key, value);
      return true;
    }
    return eqic(old, value);
  }

  public static boolean jmatchAny(String pat, Iterable<String> l) {
    return jmatch_any(pat, l);
  }

  public static boolean jmatchAny(String pat, Iterable<String> l, Matches m) {
    return jmatch_any(pat, l, m);
  }

  public static boolean jmatchAny(List<String> patterns, Iterable<String> l) {
    return jmatch_any(patterns, l, null);
  }

  public static boolean jmatchAny(List<String> patterns, Iterable<String> l, Matches m) {
    return jmatch_any(patterns, l, m);
  }

  public static boolean mechCISetContains(String listName, String a) {
    return mechCISet(listName).contains(a);
  }

  public static Object callAndMake_orF1(Object function, Object... args) {
    if (function instanceof F1)
      return callF(function, args);
    if (function == null)
      return null;
    return callAndMake_orDirect((String) function, args);
  }

  public static List<Matches> getJMatches_all(String pat, Iterable<String> l) {
    Matches m = new Matches();
    List<Matches> out = new ArrayList();
    for (String s : unnull(l)) if (jmatch(pat, s, m)) {
      out.add(m);
      m = new Matches();
    }
    return out;
  }

  public static double parseDouble(String s) {
    return Double.parseDouble(s);
  }

  public static boolean isBetween_double(double x, double min, double max) {
    return x >= min && x <= max;
  }

  public static volatile int silentException_count;

  public static volatile Throwable silentException_lastException;

  public static Throwable silentException(Throwable e) {
    ++silentException_count;
    lastException(e);
    return silentException_lastException = e;
  }

  public static Throwable lastSilentException() {
    return silentException_lastException;
  }

  public static Object callAndMake_orF2(Object function, Object... args) {
    if (function instanceof F2)
      return callF(function, args);
    if (function == null)
      return null;
    return callAndMake_orDirect((String) function, args);
  }

  public static <A, B> boolean containsKeysExcept(Map<A, B> map, Set<A> set) {
    if (map != null)
      for (A a : keys(map)) if (!contains(set, a))
        return true;
    return false;
  }

  public static Map mapMinus(Map map, Object... keys) {
    if (empty(keys))
      return map;
    Map m2 = cloneMap(map);
    for (Object key : keys) m2.remove(key);
    return m2;
  }

  public static String joinWithVBar(Iterable<String> l) {
    return join(" | ", l);
  }

  public static <A> List<A> sortByMethodDesc(Collection<A> c, final String method) {
    return sortByCalculatedFieldDesc(c, new F1<A, Object>() {

      public Object get(A a) {
        try {
          return callOpt(a, method);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "callOpt(a, method)";
      }
    });
  }

  public static <A> List<A> sortByMethodDesc(String method, Collection<A> c) {
    return sortByMethodDesc(c, method);
  }

  public static List<RuleEngine2_MatchedRule> ai_ruleEngine2_rulesForInput_5(RuleEngine2 engine, Map<String, String> inputsByType, Object... __) {
    boolean debug = boolPar("debug", __);
    List<RuleEngine2_MatchedRule> l = new ArrayList();
    for (RuleEngine2.Rule rule : engine.rules) {
      if (rule instanceof RuleEngine2.SimplifyWithRule) {
        if (((RuleEngine2.SimplifyWithRule) rule).isSplitRule)
          continue;
        String in = inputsByType.get("standard");
        Object f = ((RuleEngine2.SimplifyWithRule) rule).function();
        if (debug)
          print("Running simplifier " + ((RuleEngine2.SimplifyWithRule) rule).globalID + ": " + f);
        Object arg = ((RuleEngine2.SimplifyWithRule) rule).callOnTree ? optPar("tree", __) : in;
        if (arg == null)
          continue;
        Object result = f instanceof String ? arg instanceof String ? callF(engine.callFunctionOnString, (String) f, arg) : callAndMake((String) f, arg) : callF(f, arg);
        if (result == null)
          continue;
        List<String> list = result instanceof Collection ? allToString((Collection) result) : ll(str(result));
        if (debug)
          print("Simplifier returned: " + list);
        theLoop: for (String x : list) {
          if (nempty(x) && neq(x, in)) {
            final RuleEngine2_MatchedRule matched = new RuleEngine2_MatchedRule();
            matched.rule = ((RuleEngine2.SimplifyWithRule) rule);
            matched.out = x;
            matched.inputsByType = inputsByType;
            for (Object massager : unnull(matched.rule.mapMassagers)) if (callF(massager, new HashMap(), null, null, matched) == null) {
              if (debug)
                print("mapMassager returned null: " + massager);
              continue theLoop;
            }
            l.add(matched);
          }
        }
        continue;
      }
      final RuleEngine2_MatchedRule matched = new RuleEngine2_MatchedRule();
      matched.rule = rule;
      matched.inputsByType = inputsByType;
      matched.map = ciMap();
      matched.tokenizationFunction = or(matchAny_firstGroup("tokenize with *", rule.comments), "javaTokNPunctuationWithBrackets");
      matched.outTokenizer = matchAny_firstGroup("tokenize out with *", rule.comments);
      if (!ai_ruleEngine2_matchStep(matched, inputsByType, true, __))
        continue;
      l.add(matched);
    }
    return l;
  }

  public static List<RuleEngine2_MatchedRule> ai_ruleEngine2_rulesForInput_5_allConditions(RuleEngine2 engine, Map<String, String> inputsByType, Object... __) {
    List<RuleEngine2_MatchedRule> notDone = ai_ruleEngine2_rulesForInput_5(engine, inputsByType, __);
    List<RuleEngine2_MatchedRule> done = new ArrayList();
    boolean debug = boolPar("debug", __);
    while (nempty(notDone)) {
      List<RuleEngine2_MatchedRule> l = notDone;
      notDone = new ArrayList();
      for (RuleEngine2_MatchedRule mr : l) {
        if (!mr.moreConditions()) {
          if (debug)
            print("Done: " + mr.ruleID());
          done.add(mr);
        } else {
          if (debug)
            print("Matching step " + mr.iCond + " in " + mr.ruleID());
          if (ai_ruleEngine2_matchStep(mr, inputsByType, false)) {
            if (debug)
              print("Step succeeded");
            notDone.add(mr);
          } else
            done.add(mr);
        }
      }
    }
    return done;
  }

  public static <A> TreeMap<String, A> caseInsensitiveMap() {
    return new TreeMap(caseInsensitiveComparator());
  }

  public static <A> boolean containsAny(Collection<A> a, Collection<A> b) {
    if (empty(a) || empty(b))
      return false;
    Set<A> set = asSet(b);
    for (A x : a) if (contains(set, x))
      return true;
    return false;
  }

  public static <A, B> boolean containsAny(Map<A, B> a, Collection<A> b) {
    if (empty(a))
      return false;
    if (b != null)
      for (A x : b) if (a.containsKey(x))
        return true;
    return false;
  }

  public static <A> List<A> sortByCalculatedFieldDesc_inPlace(List<A> l, final Object f) {
    sort(l, new Comparator<A>() {

      public int compare(A b, A a) {
        return stdcompare((Object) callF(f, a), (Object) callF(f, b));
      }
    });
    return l;
  }

  public static <A> List<A> sortByCalculatedFieldDesc_inPlace(Object f, List<A> c) {
    return sortByCalculatedFieldDesc_inPlace(c, f);
  }

  public static String beautifyStructure(String s) {
    List<String> tok = javaTok(s);
    structure_addTokenMarkers(tok);
    jreplace(tok, "lhm", "");
    return join(tok);
  }

  public static String struct_noStringSharing(Object o) {
    structure_Data d = new structure_Data();
    d.noStringSharing = true;
    return structure(o, d);
  }

  public 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;
  }

  public 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;
  }

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

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

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

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

  public static String dm_gazelle_fulfillmentsCRUD() {
    return dm_require("#1022289/StatementFulfillments");
  }

  public static <B> TreeMap<String, B> asCaseInsensitiveMap(Map<String, B> map) {
    if (isCIMap(map))
      return (TreeMap) map;
    TreeMap<String, B> m = ciMap();
    m.putAll(map);
    return m;
  }

  public static Map safeUnstructureMap(String s) {
    return (Map) safeUnstructure(s);
  }

  public static <A> boolean canPutAllStrictlyIC(Map<A, String> map, Map<A, String> map2) {
    {
      final Map<A, String> __174 = unnull(map2);
      for (A key : keys(__174)) {
        String value = __174.get(key);
        if (!canPutStrictlyIC(map, key, value))
          return false;
      }
    }
    return true;
  }

  public static <A, B> Map<A, B> mergeMaps(Map<A, B>... maps) {
    Map<A, B> map = null;
    for (Map<A, B> m : maps) {
      if (map == null)
        map = similarEmptyMap(m);
      map.putAll(m);
    }
    return map;
  }

  public static List<String> ai_applyWordSplitPattern1(List<String> tokP, List<String> tokI) {
    List<Integer> indices = indicesOfSubList(tokP, ll("", "{}", ""));
    if (empty(indices))
      return tokI;
    List<String> tokI_original = tokI;
    tokI = cloneList(tokI);
    for (int i : indices) {
      String l = get(tokP, i - 1), r = get(tokP, i + 3);
      if (startsWith(l, "$") && isIdentifier(l) && isIdentifier(r)) {
        String x = get(tokI, i - 1);
        if (!ewic(x, r))
          return tokI_original;
        tokI.set(i - 1, dropLast(x, l(r)));
        tokI.add(i, "");
        tokI.add(i + 1, "{}");
        tokI.add(i + 2, "");
        tokI.add(i + 3, takeLast(x, l(r)));
      }
    }
    return tokI;
  }

  public static Map<String, String> zipCodeTokensToCIMap_strict_withoutEquals(List<String> tok1, List<String> tok2) {
    int n = l(tok1);
    if (n != l(tok2))
      return null;
    Map<String, String> map = ciMap();
    for (int i = 1; i < n; i += 2) if (!strictPutICIfKeyNotEqualToValue(map, tok1.get(i), tok2.get(i)))
      return null;
    return map;
  }

  public static boolean gazelle_addMatch(RuleEngine2_MatchedRule mr, Map<String, String> map, List<String> tokC, List<String> tokI, Object... __) {
    boolean debug = boolPar("debug", __);
    for (Object massager : unnull(mr.rule.mapMassagers)) {
      boolean wasNull = map == null;
      map = (Map<String, String>) callF(massager, map, tokC, tokI, mr);
      if (debug && map == null && !wasNull)
        print("mapMassager returned null: " + massager);
    }
    if (map == null)
      return false;
    if (!addMapToMapWithoutOverwritingIC(mr.map, map)) {
      if (debug)
        print("Adding map failed");
      return false;
    }
    mr.flush();
    return true;
  }

  public static String strWithIdentityHashCode(Object o) {
    return o == null ? "null" : "[" + identityHashCode(o) + "] " + str(o);
  }

  public static String nullIfEmpty(String s) {
    return isEmpty(s) ? null : s;
  }

  public static Map nullIfEmpty(Map map) {
    return isEmpty(map) ? null : map;
  }

  public static <A> List<A> nullIfEmpty(List<A> l) {
    return isEmpty(l) ? null : l;
  }

  public static Map<Pair<String, String>, Integer> ai_ruleEngine2_mappingQualities(RuleEngine2.Rule rule) {
    Map<Pair<String, String>, Integer> map = new HashMap();
    Matches m = new Matches();
    for (String s : rule.comments) if (match("changing * to * is *", s, m)) {
      int value = eqic(m.unq(0), "ok") ? 1 : eqic(m.unq(0), "bad") ? -1 : 0;
      map.put(pair(m.unq(1), m.unq(2)), value);
    }
    return map;
  }

  public static boolean startsWithEndsWith(String s, String prefix, String suffix) {
    return startsWith(s, prefix) && endsWith(s, suffix);
  }

  public static <B, A extends Collection<B>> A printLinesWithIndent(String indent, A lines) {
    if (lines != null)
      for (B s : lines) printIndent(indent, s);
    return lines;
  }

  public static String defaultIndent() {
    return indent(indent_default);
  }

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

  public static List<Matches> matchAll(String pat, Iterable<String> l) {
    List<Matches> l2 = new ArrayList();
    Matches m = new Matches();
    if (l != null)
      for (String s : l) if (match(pat, s, m)) {
        l2.add(m);
        m = new Matches();
      }
    return l2;
  }

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

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

  public static <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;
  }

  public static int parseIntOpt(String s) {
    return isInteger(s) ? parseInt(s) : 0;
  }

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

  public static ExpiringMap2<String, String> mechList_raw_cache = new ExpiringMap2(10000).setMap(ciMap());

  public static Lock mechList_raw_lock = lock();

  public static int mechList_raw_timeout = 60000;

  public static String mechList_raw(String name) {
    try {
      Lock __1624 = mechList_raw_lock;
      lock(__1624);
      try {
        if (empty(name))
          return "";
        String src = mechList_raw_cache.get(name);
        if (src != null)
          return src;
        src = mechList_raw_fresh(name);
        if (src != null)
          mechList_raw_cache.put(name, src);
        return src;
      } finally {
        unlock(__1624);
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static VF2<String, Object> mechList_raw_listener = new VF2<String, Object>() {

    public void get(String msg, Object arg) {
      try {
        if (eq(msg, "mechChange") && arg instanceof String) {
          mechList_raw_cache.remove((String) arg);
        }
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }

    public String toString() {
      return "if (eq(msg, 'mechChange) && arg instanceof S) {\r\n    //print(\"Got change noti...";
    }
  };

  public static void _onLoad_mechList_raw() {
    add(vm_busListeners_live(), mechList_raw_listener);
  }

  public static void cleanMeUp_mechList_raw() {
    remove(vm_busListeners_live(), mechList_raw_listener);
  }

  public static boolean longIsInt(long l) {
    return l == (int) l;
  }

  public static RuntimeException asQuickException(Throwable t) {
    return t instanceof RuntimeException ? (RuntimeException) t : new QuickException(t);
  }

  public static boolean hasMethod(Object o, String method, Object... args) {
    return findMethod_cached(o, method, args) != null;
  }

  public static ThreadLocal makeAndCall_initChild = new ThreadLocal();

  public static Object makeAndCall_cached(String functionName, Object... args) {
    Class c = loadFunctions_cached(functionName);
    AutoCloseable closer = (AutoCloseable) (callF(makeAndCall_initChild.get(), c));
    AutoCloseable __2016 = closer;
    try {
      return preciseCall(c, functionName, args);
    } finally {
      _close(__2016);
    }
  }

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

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

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

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

  public static 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)
        value = ((Long) value).intValue();
      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;
    }
  }

  public static boolean odd(int i) {
    return (i & 1) != 0;
  }

  public static boolean odd(long i) {
    return (i & 1) != 0;
  }

  public static boolean odd(BigInteger i) {
    return odd(toInt(i));
  }

  public static File tempFileFor(File f) {
    return new File(f.getPath() + "_temp");
  }

  public static void 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();
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static FileOutputStream newFileOutputStream(File path) throws IOException {
    return newFileOutputStream(path.getPath());
  }

  public static FileOutputStream newFileOutputStream(String path) throws IOException {
    return newFileOutputStream(path, false);
  }

  public static FileOutputStream newFileOutputStream(File path, boolean append) throws IOException {
    return newFileOutputStream(path.getPath(), append);
  }

  public static FileOutputStream newFileOutputStream(String path, boolean append) throws IOException {
    mkdirsForFile(path);
    FileOutputStream f = new FileOutputStream(path, append);
    _registerIO(f, path, true);
    return f;
  }

  public static boolean structure_showTiming, structure_checkTokenCount;

  public static String structure(Object o) {
    return structure(o, new structure_Data());
  }

  public static 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;
  }

  public static void structure_go(Object o, structure_Data d) {
    structure_1(o, d);
    while (nempty(d.stack)) popLast(d.stack).run();
  }

  public static void structureToPrintWriter(Object o, PrintWriter out) {
    structure_Data d = new structure_Data();
    d.out = out;
    structure_go(o, d);
  }

  public static boolean structure_allowShortening = false;

  public static class structure_Data {

    public PrintWriter out;

    public int stringSizeLimit;

    public int shareStringsLongerThan = 20;

    public boolean noStringSharing;

    public IdentityHashMap<Object, Integer> seen = new IdentityHashMap();

    public HashMap<String, Integer> strings = new HashMap();

    public HashSet<String> concepts = new HashSet();

    public HashMap<Class, List<Field>> fieldsByClass = new HashMap();

    public HashMap<Class, Field> 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 static void structure_1(final Object o, final structure_Data d) {
    try {
      if (o == null) {
        d.append("null");
        return;
      }
      Class c = o.getClass();
      boolean concept = false;
      concept = o instanceof Concept;
      List<Field> lFields = d.fieldsByClass.get(c);
      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 ", 2);
            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;
        }
        if (o instanceof File) {
          d.append("File ").append(quote(((File) o).getPath()));
          return;
        }
        Integer ref = d.seen.get(o);
        if (o instanceof String && ref == null)
          ref = d.strings.get((String) o);
        if (ref != null) {
          d.append("t").app(ref);
          return;
        }
        if (!(o instanceof String))
          d.seen.put(o, d.n);
        else {
          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;
        }
        if (o instanceof Set) {
          if (((Set) o) instanceof TreeSet) {
            d.append(isCISet_gen(((Set) o)) ? "ciset" : "treeset");
            structure_1(new ArrayList(((Set) o)), d);
            return;
          }
          d.append(((Set) o) instanceof LinkedHashSet ? "lhs" : "hashset");
          structure_1(new ArrayList(((Set) o)), d);
          return;
        }
        String name = c.getName();
        if (o instanceof Collection && !startsWith(name, "main$")) {
          if (name.equals("java.util.Collections$SynchronizedList") || name.equals("java.util.Collections$SynchronizedRandomAccessList"))
            d.append("sync");
          d.append("[");
          final int l = d.n;
          final Iterator it = ((Collection) 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.sta...";
            }
          });
          return;
        }
        if (o instanceof Map && !startsWith(name, "main$")) {
          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"))
            d.append("sync");
          else if (name.equals("java.util.Collections$SynchronizedSortedMap")) {
            d.append("sync tm", 2);
          }
          d.append("{");
          final int l = d.n;
          final Iterator it = ((Map) o).entrySet().iterator();
          d.stack.add(new Runnable() {

            public boolean v;

            public Map.Entry e;

            public void run() {
              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);
                }
              }
            }
          });
          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;
          }
          String atype = "array", sep = ", ";
          if (o instanceof int[]) {
            atype = "intarray";
            sep = " ";
          }
          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;
        }
        if (name.startsWith("java.") || name.startsWith("javax.")) {
          d.append("j ").append(quote(str(o)));
          return;
        }
        String dynName = shortDynamicClassName(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)) {
            String fieldName = field.getName();
            if (fieldName.equals("_persistenceInfo"))
              d.persistenceInfo.put(c, field);
            if ((field.getModifiers() & (java.lang.reflect.Modifier.STATIC | java.lang.reflect.Modifier.TRANSIENT)) != 0)
              continue;
            fields.add(field);
          }
          cc = cc.getSuperclass();
        }
        lFields = asList(fields);
        for (int i = 0; i < l(lFields); i++) {
          Field f = lFields.get(i);
          if (f.getName().equals("this$1")) {
            lFields.remove(i);
            lFields.add(0, f);
            break;
          }
        }
        d.fieldsByClass.put(c, lFields);
      } else {
        Integer ref = d.seen.get(o);
        if (ref != null) {
          d.append("t").app(ref);
          return;
        }
        d.seen.put(o, d.n);
      }
      Field persistenceInfoField = (Field) (d.persistenceInfo.get(c));
      Map<String, Object> persistenceInfo = persistenceInfoField == null ? null : (Map) persistenceInfoField.get(o);
      LinkedHashMap<String, Object> fv = new LinkedHashMap();
      for (Field f : lFields) {
        Object value;
        try {
          value = f.get(o);
        } catch (Exception e) {
          value = "?";
        }
        if (value != null && (persistenceInfo == null || !Boolean.FALSE.equals(persistenceInfo.get(f.getName()))))
          fv.put(f.getName(), value);
      }
      String name = c.getName();
      String shortName = dropPrefix("main$", name);
      if (startsWithDigit(shortName))
        shortName = name;
      if (concept && eq(fv.get("className"), shortName))
        fv.remove("className");
      if (o instanceof DynamicObject) {
        fv.putAll((Map) fv.get("fieldValues"));
        fv.remove("fieldValues");
        shortName = shortDynamicClassName(o);
        fv.remove("className");
      }
      String singleField = fv.size() == 1 ? first(fv.keySet()) : null;
      d.append(shortName);
      final int l = d.n;
      final Iterator it = fv.entrySet().iterator();
      d.stack.add(new Runnable() {

        public void run() {
          try {
            if (!it.hasNext()) {
              if (d.n != l)
                d.append(")");
            } else {
              Map.Entry e = (Map.Entry) it.next();
              d.append(d.n == l ? "(" : ", ");
              d.append((String) e.getKey()).append("=");
              d.stack.add(this);
              structure_1(e.getValue(), d);
            }
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "if (!it.hasNext()) {\r\n      if (d.n != l)\r\n        d.append(\")\");\r\n    } else...";
        }
      });
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static String fileInfo(File f) {
    return renderFileInfo(f);
  }

  public static String fileInfo(String f) {
    return renderFileInfo(newFile(f));
  }

  public static String uniqueFileNameUsingMD5_80_v2(String fullName) {
    return uniqueFileNameUsingMD5_80_v2(fullName, md5(fullName));
  }

  public static String uniqueFileNameUsingMD5_80_v2(String fullName, String md5) {
    return takeFirst(80 - 33, fileNameEncode(fullName)) + " - " + md5;
  }

  public static String dm_gazelle_longFeedbackCRUD() {
    return dm_require("#1021787/AppliedRules");
  }

  public static <A> A[] makeArray(Class<A> type, int n) {
    return (A[]) Array.newInstance(type, n);
  }

  public static Map<String, List<String>> javaTokNPunctuationWithBrackets_cache = synchronizedMRUCache(1000);

  public static List<String> javaTokNPunctuationWithBrackets(String s) {
    List<String> tok = javaTokNPunctuationWithBrackets_cache.get(s);
    if (tok == null)
      javaTokNPunctuationWithBrackets_cache.put(s, tok = javaTokNPunctuationWithBrackets_uncached(s));
    return tok;
  }

  public static List<String> javaTokNPunctuationWithBrackets_uncached(String s) {
    return tok_combineRoundOrCurlyBrackets_keep(javaTokNPunctuation_bracketsInC(s));
  }

  public 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);
  }

  public static 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;
  }

  public static boolean sameSnippetID(String a, String b) {
    if (!isSnippetID(a) || !isSnippetID(b))
      return false;
    return parseSnippetID(a) == parseSnippetID(b);
  }

  public static File javaxDataDir_dir;

  public static File javaxDataDir() {
    return javaxDataDir_dir != null ? javaxDataDir_dir : new File(userHome(), "JavaX-Data");
  }

  public static File javaxDataDir(String... subs) {
    return newFile(javaxDataDir(), subs);
  }

  public static volatile String caseID_caseID;

  public static String caseID() {
    return caseID_caseID;
  }

  public static void caseID(String id) {
    caseID_caseID = id;
  }

  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;
  }

  public static TimeZone getTimeZone(String name) {
    return TimeZone.getTimeZone(name);
  }

  public static String standardTimeZone_name = "Europe/Berlin";

  public static String standardTimeZone() {
    return standardTimeZone_name;
  }

  public static String assertIsIdentifier(String s) {
    if (!isIdentifier(s))
      throw fail("Not an identifier: " + quote(s));
    return s;
  }

  public static 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);";
      }
    };
  }

  public static BufferedReader utf8BufferedReader(InputStream in) {
    return utf8bufferedReader(in);
  }

  public static BufferedReader utf8BufferedReader(File f) {
    return utf8bufferedReader(f);
  }

  public static int gzInputStream_defaultBufferSize = 65536;

  public static GZIPInputStream gzInputStream(File f) {
    try {
      return gzInputStream(new FileInputStream(f));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static GZIPInputStream gzInputStream(File f, int bufferSize) {
    try {
      return gzInputStream(new FileInputStream(f), bufferSize);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static GZIPInputStream gzInputStream(InputStream in) {
    return gzInputStream(in, gzInputStream_defaultBufferSize);
  }

  public static GZIPInputStream gzInputStream(InputStream in, int bufferSize) {
    try {
      return _registerIOWrap(new GZIPInputStream(in, gzInputStream_defaultBufferSize), in);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static Object unstructure(String text) {
    return unstructure(text, false);
  }

  public static Object unstructure(String text, final boolean allDynamic) {
    return unstructure(text, allDynamic, null);
  }

  public static int structure_internStringsLongerThan = 50;

  public static int unstructure_unquoteBufSize = 100;

  public static int unstructure_tokrefs;

  public abstract static class unstructure_Receiver {

    public abstract void set(Object o);
  }

  public static Object unstructure(String text, boolean allDynamic, Object classFinder) {
    if (text == null)
      return null;
    return unstructure_tok(javaTokC_noMLS_iterator(text), allDynamic, classFinder);
  }

  public static Object unstructure_reader(BufferedReader reader) {
    return unstructure_tok(javaTokC_noMLS_onReader(reader), false, null);
  }

  public static Object unstructure_tok(final Producer<String> tok, final boolean allDynamic, final Object _classFinder) {
    final boolean debug = unstructure_debug;
    final class X {

      public int i = -1;

      public final Object classFinder = _classFinder != null ? _classFinder : _defaultClassFinder();

      public HashMap<Integer, Object> refs = new HashMap();

      public HashMap<Integer, Object> tokrefs = new HashMap();

      public HashSet<String> concepts = new HashSet();

      public HashMap<String, Class> classesMap = new HashMap();

      public List<Runnable> stack = new ArrayList();

      public String curT;

      public char[] unquoteBuf = new char[unstructure_unquoteBufSize];

      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 = 0;
        if (structure_isMarker(t, 0, l(t))) {
          refID = parseInt(t.substring(1));
          consume();
        }
        final int _refID = refID;
        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, final unstructure_Receiver out) {
        String t = t();
        Class c = classesMap.get(t);
        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("bigint")) {
            out.set(parseBigInt());
            return;
          }
          if (t.equals("d")) {
            out.set(parseDouble());
            return;
          }
          if (t.equals("fl")) {
            out.set(parseFloat());
            return;
          }
          if (t.equals("sh")) {
            consume();
            t = tpp();
            if (t.equals("-")) {
              t = tpp();
              out.set((short) (-parseInt(t)));
              return;
            }
            out.set((short) parseInt(t));
            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("false") || t.equals("f")) {
            consume();
            out.set(false);
            return;
          }
          if (t.equals("true") || t.equals("t")) {
            consume();
            out.set(true);
            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)
              print("Warning: 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)
              print("Warning: unsatisfied token reference " + ref);
            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("sync")) {
            consume();
            if (t().equals("tm")) {
              consume();
              {
                parseMap(synchronizedTreeMap(), out);
                return;
              }
            }
            if (t().equals("[")) {
              parseList(synchroList(), out);
              return;
            }
            {
              parseMap(synchronizedMap(), out);
              return;
            }
          }
          if (t.equals("{")) {
            parseMap(out);
            return;
          }
          if (t.equals("[")) {
            this.parseList(new ArrayList(), out);
            return;
          }
          if (t.equals("bitset")) {
            parseBitSet(out);
            return;
          }
          if (t.equals("array") || t.equals("intarray")) {
            parseArray(out);
            return;
          }
          if (t.equals("ba")) {
            consume();
            String hex = unquote(tpp());
            out.set(hexToBytes(hex));
            return;
          }
          if (t.equals("boolarray")) {
            consume();
            int n = parseInt(tpp());
            String hex = unquote(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("c");
            t = t();
            assertTrue(isJavaIdentifier(t));
            concepts.add(t);
          }
        }
        if (eq(t, "j")) {
          consume("j");
          out.set(parseJava());
          return;
        }
        if (c == null && !isJavaIdentifier(t))
          throw new RuntimeException("Unknown token " + (i + 1) + ": " + t);
        consume();
        String className, fullClassName;
        if (eq(t(), ".")) {
          consume();
          className = fullClassName = t + "." + assertIdentifier(tpp());
        } else {
          className = t;
          fullClassName = "main$" + t;
        }
        if (c == null) {
          if (allDynamic)
            c = null;
          else
            c = classFinder != null ? (Class) callF(classFinder, fullClassName) : findClass_fullName(fullClassName);
          if (c != null)
            classesMap.put(className, c);
        }
        boolean hasBracket = eq(t(), "(");
        if (hasBracket)
          consume();
        boolean hasOuter = hasBracket && eq(t(), "this$1");
        DynamicObject dO = null;
        Object o = null;
        final String thingName = t;
        if (c != null) {
          o = hasOuter ? nuStubInnerObject(c, classFinder) : nuEmptyObject(c);
          if (o instanceof DynamicObject)
            dO = (DynamicObject) o;
        } else {
          if (concepts.contains(t) && (c = findClass("Concept")) != null)
            o = dO = (DynamicObject) nuEmptyObject(c);
          else
            dO = new DynamicObject();
          dO.className = className;
        }
        if (refID != 0)
          refs.put(refID, o != null ? o : dO);
        tokrefs.put(tokIndex, o != null ? o : dO);
        final LinkedHashMap<String, Object> fields = new LinkedHashMap();
        final Object _o = o;
        final DynamicObject _dO = dO;
        if (hasBracket) {
          stack.add(new Runnable() {

            public void run() {
              try {
                if (eq(t(), ")")) {
                  consume(")");
                  objRead(_o, _dO, fields);
                  out.set(_o != null ? _o : _dO);
                } else {
                  final String key = unquote(tpp());
                  if (!eq(tpp(), "="))
                    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);
                      if (eq(t(), ","))
                        consume();
                    }
                  });
                }
              } catch (Exception __e) {
                throw rethrow(__e);
              }
            }

            public String toString() {
              return "if (eq(t(), \")\")) {\r\n            consume(\")\");\r\n            objRead(_o, _dO, ...";
            }
          });
        } else {
          objRead(o, dO, fields);
          out.set(o != null ? o : dO);
        }
      }

      public void objRead(Object o, DynamicObject dO, Map<String, Object> fields) {
        if (o != null)
          if (dO != null) {
            setOptAllDyn(dO, fields);
          } else {
            setOptAll_pcall(o, fields);
          }
        else
          for (String field : keys(fields)) dO.fieldValues.put(intern(field), fields.get(field));
        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       ...";
          }
        });
      }

      public void parseList(final List list, final unstructure_Receiver out) {
        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          out.set(list);\r\n     ...";
          }
        });
      }

      public void parseArray(final unstructure_Receiver out) {
        final String type = tpp();
        consume("{");
        final List list = new ArrayList();
        stack.add(new Runnable() {

          public void run() {
            try {
              if (eq(t(), "}")) {
                consume("}");
                out.set(type.equals("intarray") ? toIntArray(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          out.set(type.equals(\"...";
          }
        });
      }

      public Object parseClass() {
        consume("class");
        consume("(");
        String name = unquote(tpp());
        consume(")");
        Class c = allDynamic ? null : classFinder != null ? (Class) callF(classFinder, name) : findClass_fullName(name);
        if (c != null)
          return c;
        DynamicObject dO = new DynamicObject();
        dO.className = "java.lang.Class";
        name = dropPrefix("main$", name);
        dO.fieldValues.put("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;

          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_x(unstructure_Receiver out) {
        consume();
        parse(out);
        while (nempty(stack)) popLast(stack).run();
      }
    }
    Boolean b = DynamicObject_loading.get();
    DynamicObject_loading.set(true);
    try {
      final Var v = new Var();
      X x = new X();
      x.parse_x(new unstructure_Receiver() {

        public void set(Object o) {
          v.set(o);
        }
      });
      unstructure_tokrefs = x.tokrefs.size();
      return v.get();
    } finally {
      DynamicObject_loading.set(b);
    }
  }

  public static boolean unstructure_debug;

  public static 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();
  }

  public static List<Object> getMultiPorts() {
    return (List) callOpt(getJavaX(), "getMultiPorts");
  }

  public static String emptyToNull(String s) {
    return eq(s, "") ? null : s;
  }

  public static <A, B> Map<A, B> emptyToNull(Map<A, B> map) {
    return empty(map) ? null : map;
  }

  public static void startMultiPort() {
    List mp = getMultiPorts();
    if (mp != null && mp.isEmpty()) {
      nohupJavax("#1001639");
      throw fail("Upgrading JavaX, please restart this program afterwards.");
    }
  }

  public static <A> Set<A> synchroTreeSet() {
    return Collections.synchronizedSet(new TreeSet<A>());
  }

  public static boolean forbiddenPort(int port) {
    return port == 5037;
  }

  public static <A> A getAndClearThreadLocal(ThreadLocal<A> tl) {
    A a = tl.get();
    tl.set(null);
    return a;
  }

  public static String defaultThreadName_name;

  public static String defaultThreadName() {
    if (defaultThreadName_name == null)
      defaultThreadName_name = "A thread by " + programID();
    return defaultThreadName_name;
  }

  public static Runnable wrapAsActivity(Object r) {
    return toRunnable(r);
  }

  public static Thread newThread(Object runnable) {
    return new Thread(_topLevelErrorHandling(toRunnable(runnable)));
  }

  public static Thread newThread(Object runnable, String name) {
    if (name == null)
      name = defaultThreadName();
    return new Thread(_topLevelErrorHandling(toRunnable(runnable)), name);
  }

  public static Thread newThread(String name, Object runnable) {
    return newThread(runnable, name);
  }

  public static Runnable toRunnable(final Object o) {
    if (o instanceof Runnable)
      return (Runnable) o;
    return new Runnable() {

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

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

  public static Map<Thread, Boolean> _registerThread_threads;

  public static Object _onRegisterThread;

  public static 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;
  }

  public static void _registerThread() {
    _registerThread(Thread.currentThread());
  }

  public static 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);
    }
  }

  public static String makeRandomID(int length) {
    return makeRandomID(length, defaultRandomGenerator());
  }

  public static 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);
  }

  public static Map vm_generalMap_map;

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

  public static String getProgramName_cache;

  public static String getProgramName() {
    Lock __1957 = downloadLock();
    lock(__1957);
    try {
      if (getProgramName_cache == null)
        getProgramName_cache = getSnippetTitleOpt(programID());
      return getProgramName_cache;
    } finally {
      unlock(__1957);
    }
  }

  public static void _onLoad_getProgramName() {
    startThread(new Runnable() {

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

      public String toString() {
        return "getProgramName();";
      }
    });
  }

  public static Object callOptMC(String method, Object... args) {
    return callOpt(mc(), method, args);
  }

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

  public static <A> Set<A> synchronizedSet(Set<A> set) {
    return Collections.synchronizedSet(set);
  }

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

  public static <A, B, C> T3<A, B, C> t3(A a, B b, C c) {
    return new T3(a, b, c);
  }

  public static 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();
  }

  public static List<String> tok_combineRoundCurlySquareBrackets_keep(List<String> tok) {
    List<String> l = new ArrayList();
    for (int i = 0; i < l(tok); i++) {
      String t = tok.get(i);
      if (odd(i) && eqOneOf(t, "{", "(", "[")) {
        int j = findEndOfBracketPart2(tok, i);
        l.add(joinSubList(tok, i, j));
        i = j - 1;
      } else
        l.add(t);
    }
    return l;
  }

  public static boolean matchEnd(String pat, String s) {
    return matchEnd(pat, s, null);
  }

  public static boolean matchEnd(String pat, String s, Matches matches) {
    if (s == null)
      return false;
    List<String> tokpat = parse3(pat), toks = parse3(s);
    if (toks.size() < tokpat.size())
      return false;
    String[] m = match2(tokpat, takeLast(l(tokpat), toks));
    if (m == null)
      return false;
    if (matches != null) {
      matches.m = new String[m.length + 1];
      arraycopy(m, matches.m);
      matches.m[m.length] = join(dropLast(l(tokpat), toks));
    }
    return true;
  }

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

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

  public static List<String> mechList_opt(String name) {
    return mechList_opt_tlft(name);
  }

  public static <A, B> HashMap<A, B> asHashMap(Map<A, B> map) {
    if (map instanceof HashMap)
      return (HashMap) map;
    HashMap<A, B> m = new HashMap();
    m.putAll(map);
    return m;
  }

  public static Map<String, String> parseDoubleArrowMap(Map<String, String> map, List<String> l) {
    for (String s : l) {
      List<String> x = splitAtDoubleArrow_bothDirections(s);
      if (l(x) == 2)
        map.put(first(x), second(x));
    }
    return map;
  }

  public static List similarEmptyList(Collection m) {
    return new ArrayList();
  }

  public static List<String> splitAtSlash(String s) {
    return trimAll(asList(s.split("/")));
  }

  public static List filterByProgramIDAndShortClassName(Iterable l, String progID, String className) {
    List out = new ArrayList();
    for (Object o : unnull(l)) if (shortClassNameIs(o, className) && sameSnippetID(getProgramID(o), progID))
      out.add(o);
    return out;
  }

  public static List dm_listModules() {
    return (List) callCreator("listModules");
  }

  public static List filterByShortClassName(Iterable l, String name) {
    List out = new ArrayList();
    for (Object o : unnull(l)) if (shortClassNameIs(o, name))
      out.add(o);
    return out;
  }

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

  public static <A> Set<A> synchroSet(Set<A> set) {
    return Collections.synchronizedSet(set);
  }

  public static <A> Set<A> newWeakHashSet() {
    return synchroWeakHashSet();
  }

  public static List beforeDelegatingToThread_operations = synchroList();

  public static void beforeDelegatingToThread(Thread t) {
    for (Object op : cloneList(beforeDelegatingToThread_operations)) pcallF(op, t);
  }

  public static void beforeDelegatingToThread_do(Object f) {
    setAdd(beforeDelegatingToThread_operations, f);
  }

  public static void cancelAndInterruptThread(Thread t) {
    if (t == null)
      return;
    cancelThread(t);
    t.interrupt();
  }

  public static <A, B> Either<A, B> either2(B b) {
    return new Either(2, b);
  }

  public static <A, B> Either<A, B> either1(A a) {
    return new Either(1, a);
  }

  public static List afterDelegatingToThread_operations = synchroList();

  public static void afterDelegatingToThread(Thread t) {
    for (Object op : cloneList(afterDelegatingToThread_operations)) pcallF(op, t);
  }

  public static void afterDelegatingToThread_do(Object f) {
    setAdd(afterDelegatingToThread_operations, f);
  }

  public static boolean tok_shouldAddReturn(List<String> tok) {
    String lastToken = get(tok, l(tok) - 2);
    return !eqOneOf(lastToken, "}", ";");
  }

  public static boolean tok_shouldAddReturn(String s) {
    return tok_shouldAddReturn(javaTok(s));
  }

  public static boolean isKnownVoidFunction(String name) {
    return standardFunctionAlwaysReturnsVoid(name);
  }

  public static void tokAppend_reTok(List<String> tok, int i, String s) {
    tok.set(i, tok.get(i) + s);
    reTok(tok, i, i + 1);
  }

  public static String tok_addReturn(List<String> tok) {
    if (tok_shouldAddReturn(tok)) {
      tokPrepend(tok, 1, "ret ");
      tokAppend(tok, l(tok) - 2, ";");
    }
    return join(tok);
  }

  public static String tok_addReturn(String s) {
    return tok_addReturn(javaTok(s));
  }

  public static boolean containsReturnWithArgument(List<String> tok) {
    for (int i = 1; i + 2 < l(tok); i += 2) if (eqOneOf(tok.get(i), "ret", "return") && neq(tok.get(i + 2), ";"))
      return true;
    return false;
  }

  public static boolean containsReturnWithArgument(String code) {
    return containsReturnWithArgument(javaTok(code));
  }

  public static boolean veryQuickJava_silent = true;

  public static boolean veryQuickJava_useCompilerBot = true;

  public static ThreadLocal<String> veryQuickJava_transpiled = new ThreadLocal();

  public static Object veryQuickJava3_cacheFunction;

  public static Class veryQuickJava3(String mainJava) {
    return veryQuickJava3(mainJava, emptyList());
  }

  public static Class veryQuickJava3(String mainJava, List<String> libs) {
    Class c = (Class) (callF(veryQuickJava3_cacheFunction, mainJava, libs));
    if (c != null)
      return c;
    transpileRaw_silent = veryQuickJava_silent;
    String src = transpileRaw(mainJava);
    if (veryQuickJava_transpiled.get() != null)
      veryQuickJava_transpiled.set(src);
    return veryQuickJava_finish(src, libs);
  }

  public static List<Class> dependentClasses() {
    return cleanUpAndGetWeakReferencesList(hotwire_classes);
  }

  public static Object callCalc(Object o, Object... args) {
    return call(o, "calc", args);
  }

  public static int findEndOfCurlyBracketPart(List<String> cnc, int i) {
    int j = i + 2, level = 1;
    while (j < cnc.size()) {
      if (eq(cnc.get(j), "{"))
        ++level;
      else if (eq(cnc.get(j), "}"))
        --level;
      if (level == 0)
        return j + 1;
      ++j;
    }
    return cnc.size();
  }

  public static String upper(String s) {
    return s == null ? null : s.toUpperCase();
  }

  public static char upper(char c) {
    return Character.toUpperCase(c);
  }

  public static List<String> parse3(String s) {
    return dropPunctuation(javaTokPlusPeriod(s));
  }

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

  public static boolean equalsIgnoreCase(char a, char b) {
    return eqic(a, b);
  }

  public static CloseableIterableIterator emptyCloseableIterableIterator_instance = new CloseableIterableIterator() {

    public Object next() {
      throw fail();
    }

    public boolean hasNext() {
      return false;
    }
  };

  public static <A> CloseableIterableIterator<A> emptyCloseableIterableIterator() {
    return emptyCloseableIterableIterator_instance;
  }

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

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

  public static CloseableIterableIterator<String> linesFromReader(Reader r) {
    final BufferedReader br = bufferedReader(r);
    return iteratorFromFunction_f0_autoCloseable(new F0<String>() {

      public String get() {
        try {
          return readLineFromReaderWithClose(br);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "ret readLineFromReaderWithClose(br);";
      }
    }, _wrapIOCloseable(r));
  }

  public static BufferedReader utf8bufferedReader(InputStream in) {
    try {
      return bufferedReader(_registerIOWrap(new InputStreamReader(in, "UTF-8"), in));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static BufferedReader utf8bufferedReader(File f) {
    try {
      return utf8bufferedReader(newFileInputStream(f));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static GZIPInputStream newGZIPInputStream(File f) {
    return gzInputStream(f);
  }

  public static GZIPInputStream newGZIPInputStream(InputStream in) {
    return gzInputStream(in);
  }

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

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

  public static Class getClass(Object realm, String name) {
    try {
      try {
        return getClass(realm).getClassLoader().loadClass(classNameToVM(name));
      } catch (ClassNotFoundException e) {
        return null;
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static void logQuotedWithTime(String s) {
    logQuotedWithTime(standardLogFile(), s);
  }

  public static void logQuotedWithTime(File logFile, String s) {
    logQuoted(logFile, logQuotedWithTime_format(s));
  }

  public static void logQuotedWithTime(String logFile, String s) {
    logQuoted(logFile, logQuotedWithTime_format(s));
  }

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

  public static Component wrap(Object swingable) {
    return _recordNewSwingComponent(wrap_2(swingable));
  }

  public static Component wrap_2(Object swingable) {
    if (swingable == null)
      return null;
    Component c;
    if (swingable instanceof Component)
      c = (Component) swingable;
    else
      c = (Component) callOpt(swingable, "swing");
    if (c instanceof JTable || c instanceof JList || c instanceof JTextArea || c instanceof JEditorPane || c instanceof JTextPane || c instanceof JTree)
      return jscroll(c);
    return c == null ? jlabel(str(swingable)) : c;
  }

  public static JTextArea wrappedTextArea(final JTextArea ta) {
    enableWordWrapForTextArea(ta);
    return ta;
  }

  public static JTextArea wrappedTextArea() {
    return wrappedTextArea(jtextarea());
  }

  public static JTextArea wrappedTextArea(String text) {
    JTextArea ta = wrappedTextArea();
    setText(ta, text);
    return ta;
  }

  public static <A extends JComponent> A onClick(final A c, final Object runnable) {
    if (c != null) {
      swing(new Runnable() {

        public void run() {
          try {
            c.addMouseListener(new MouseAdapter() {

              public void mouseClicked(MouseEvent e) {
                callF(runnable, e);
              }
            });
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "c.addMouseListener(new MouseAdapter {\r\n      public void mouseClicked(MouseEv...";
        }
      });
    }
    return c;
  }

  public static void onClick(JButton btn, final Object runnable) {
    onEnter(btn, runnable);
  }

  public static void disposeWindow(final Window window) {
    if (window != null) {
      swing(new Runnable() {

        public void run() {
          try {
            window.dispatchEvent(new WindowEvent(window, WindowEvent.WINDOW_CLOSING));
            myFrames_list.remove(window);
            window.dispose();
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "window.dispatchEvent(new WindowEvent(window, WindowEvent.WINDOW_CLOSING)); //...";
        }
      });
    }
  }

  public static void disposeWindow(final Component c) {
    disposeWindow(getWindow(c));
  }

  public static void disposeWindow(Object o) {
    if (o != null)
      disposeWindow(((Component) o));
  }

  public static void disposeWindow() {
    disposeWindow(heldInstance(Component.class));
  }

  public static Font typeWriterFont() {
    return typeWriterFont(iround(14 * getSwingFontScale()));
  }

  public static Font typeWriterFont(int size) {
    return new Font("Courier", Font.PLAIN, size);
  }

  public static JScrollPane jscroll(final Component c) {
    return swing(new F0<JScrollPane>() {

      public JScrollPane get() {
        try {
          return new JScrollPane(c);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "ret new JScrollPane(c);";
      }
    });
  }

  public static int withMargin_defaultWidth = 6;

  public static JPanel withMargin(Component c) {
    return withMargin(withMargin_defaultWidth, c);
  }

  public static JPanel withMargin(int w, Component c) {
    return withMargin(w, w, c);
  }

  public static JPanel withMargin(int w, int h, Component c) {
    return withMargin(w, h, w, h, c);
  }

  public static JPanel withMargin(final int top, final int left, final int bottom, final int right, final Component c) {
    return swing(new F0<JPanel>() {

      public JPanel get() {
        try {
          JPanel p = new JPanel(new BorderLayout());
          p.setBorder(BorderFactory.createEmptyBorder(top, left, bottom, right));
          p.add(c);
          return p;
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "JPanel p = new JPanel(new BorderLayout);\r\n    p.setBorder(BorderFactory.creat...";
      }
    });
  }

  public static Window getWindow(Object o) {
    if (!(o instanceof Component))
      return null;
    Component c = (Component) o;
    while (c != null) {
      if (c instanceof Window)
        return (Window) c;
      c = c.getParent();
    }
    return null;
  }

  public static Dimension getScreenSize() {
    return Toolkit.getDefaultToolkit().getScreenSize();
  }

  public static void swingLater(long delay, final Object r) {
    javax.swing.Timer timer = new javax.swing.Timer(toInt(delay), actionListener(wrapAsActivity(r)));
    timer.setRepeats(false);
    timer.start();
  }

  public static void swingLater(Object r) {
    SwingUtilities.invokeLater(toRunnable(r));
  }

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

  public static Map<String, Object> objectToMap(Object o) {
    try {
      if (o instanceof Map)
        return (Map) o;
      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)
        map.putAll(((DynamicObject) o).fieldValues);
      return map;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static 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;
  }

  public static String[] asStringArray(Collection<String> c) {
    return toStringArray(c);
  }

  public static String[] asStringArray(Object o) {
    return toStringArray(o);
  }

  public static <A> List<A> reverseInPlace(List<A> l) {
    return reverseList(l);
  }

  public static Object unstructureUnlessContainsAnonymousClasses(String s) {
    return structContainsAnonymousClasses(s) ? null : unstruct(s);
  }

  public static <A, B> B getOrCreate_f1(Map<A, B> map, A key, F1<A, B> create) {
    try {
      B b = map.get(key);
      if (b == null)
        mapPut(map, key, b = callF(create, key));
      return b;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static List<String> ai_gazelle_statementsFromAppliedRule(RuleEngine2_MatchedRule r, String judgement) {
    if (r == null || r.rule == null)
      return new ArrayList();
    if (nempty(judgement) && neq(judgement, "good"))
      judgement = "bad";
    String ruleID = r.rule.globalID;
    String ruleIn = first(r.rule.in);
    if (ruleIn == null)
      return new ArrayList();
    Map<String, String> varMap = r.map;
    String tokenize = r.tokenizationFunction;
    List<String> tok = (List<String>) (callAndMake(tokenize, ruleIn));
    List<String> tokC = codeTokens(tok);
    Set<String> possibleVars = asCISet(tokC);
    List<String> statements = new ArrayList();
    for (String a : keys(varMap)) {
      String b = varMap.get(a);
      statements.add(format("in a * mapping, variable " + quote(a) + " was used", judgement));
      statements.add(format("in a * mapping, variable " + quote(a) + " was mapped to " + quote(b), judgement));
    }
    for (String var : possibleVars) statements.add("possible variable " + quote(var));
    statements.add(format("a * mapping is *", judgement, sfu(varMap)));
    return statements;
  }

  public static <A, B> Collection<B> values(Map<A, B> map) {
    return map == null ? emptyList() : map.values();
  }

  public static <A, B> Collection<B> values(MultiMap<A, B> mm) {
    return mm == null ? emptyList() : concatLists(values(mm.data));
  }

  public static <A, B> MultiSet<B> getOrCreateMultiSet(Map<A, MultiSet<B>> map, A key) {
    MultiSet<B> b = map.get(key);
    if (b == null)
      map.put(key, b = new MultiSet());
    return b;
  }

  public static class ai_gazelle_analyzeStatementsForRule_Data {

    public MultiSet<String> statements;

    public Set<String> possibleVars, usedVars, unusedVars, varsUsedInGood, varsUsedInBad, varsUsedInGoodOnly, varsUsedInBadOnly;

    public Set<String> unclearVars;

    public MultiMap<String, String> goodMappings, badMappings, alwaysGoodMappings, alwaysBadMappings;

    public List<Map<String, String>> completeGoodMappings, completeBadMappings;

    public Map<Pair<String, String>, PosNegScore> varScores;
  }

  public static ai_gazelle_analyzeStatementsForRule_Data ai_gazelle_analyzeStatementsForRule(Collection<String> l) {
    Set<String> possibleVars = asCISet(matchAny_firstGroups("possible variable *", l));
    Set<String> usedVars = asCISet(matchX_any_firstGroups("... variable * was used", l));
    Set<String> unusedVars = asCISet(listMinusSet(possibleVars, usedVars));
    Set<String> varsUsedInGood = asCISet(matchX_any_firstGroups("in a good mapping, variable * was used", l));
    Set<String> varsUsedInBad = asCISet(matchX_any_firstGroups("in a bad mapping, variable * was used", l));
    Set<String> unclearVars = setIntersection(varsUsedInGood, varsUsedInBad);
    MultiMap<String, String> goodMappings = uniquifyMultiMapValues(pairsToMultiMap(matchX_any_firstAndSecondGroups("in a good mapping, variable * was mapped to *", l)));
    MultiMap<String, String> badMappings = uniquifyMultiMapValues(pairsToMultiMap(matchX_any_firstAndSecondGroups("in a bad mapping, variable * was mapped to *", l)));
    MultiMap<String, String> alwaysGoodMappings = multiMapDiff(goodMappings, badMappings);
    MultiMap<String, String> alwaysBadMappings = multiMapDiff(badMappings, goodMappings);
    List<Map<String, String>> completeGoodMappings = map("safeUnstruct", matchX_any_firstGroups("a good mapping is *", l));
    List<Map<String, String>> completeBadMappings = map("safeUnstruct", matchX_any_firstGroups("a bad mapping is *", l));
    return nu(ai_gazelle_analyzeStatementsForRule_Data.class, "possibleVars", possibleVars, "usedVars", usedVars, "unusedVars", unusedVars, "varsUsedInGood", varsUsedInGood, "varsUsedInBad", varsUsedInBad, "unclearVars", unclearVars, "varsUsedInGoodOnly", setDiff(varsUsedInGood, varsUsedInBad), "varsUsedInBadOnly", setDiff(varsUsedInBad, varsUsedInGood), "goodMappings", goodMappings, "badMappings", badMappings, "alwaysGoodMappings", alwaysGoodMappings, "alwaysBadMappings", alwaysBadMappings, "completeGoodMappings", completeGoodMappings, "completeBadMappings", completeBadMappings);
  }

  public static <A> PosNegScore getOrCreatePosNegScore(Map<A, PosNegScore> map, A key) {
    return getOrCreate((Map) map, key, PosNegScore.class);
  }

  public static String ai_gazelle_predictor1_onData(RuleEngine2_MatchedRule r, ai_gazelle_analyzeStatementsForRule_Data data, Object... __) {
    boolean debug = boolPar("debug", __);
    String prediction = containsAny(keys(r.map), data.varsUsedInBadOnly) ? "bad" : "good";
    if (debug)
      print("predictor1: " + keys(r.map) + " => " + prediction);
    return prediction;
  }

  public static ThreadLocal<Long> saveTiming_last = new ThreadLocal();

  public static void saveTiming(long ms) {
    print(ms + " ms");
    saveTiming_noPrint(ms);
  }

  public static void saveTiming_noPrint(long ms) {
    saveTiming_last.set(ms);
  }

  public static String optionalCurlyBrace(String s) {
    return isCurlyBraced(s) ? s : curlyBrace(s);
  }

  public static String tok_unCurlyBracket(String s) {
    return isCurlyBraced(s) ? join(dropFirstThreeAndLastThree(javaTok(s))) : s;
  }

  public static boolean jmatch_any(String pat, Iterable<String> l) {
    return jmatch_any(pat, l, null);
  }

  public static boolean jmatch_any(String pat, Iterable<String> l, Matches m) {
    for (String s : unnull(l)) if (jmatch(pat, s, m))
      return true;
    return false;
  }

  public static boolean jmatch_any(List<String> patterns, Iterable<String> l, Matches m) {
    if (nempty(patterns))
      for (String s : unnull(l)) for (String pat : patterns) if (jmatch(pat, s, m))
        return true;
    return false;
  }

  public static boolean jmatch(String pat, String s) {
    return jmatch(pat, s, null);
  }

  public static boolean jmatch(String pat, String s, Matches matches) {
    if (s == null)
      return false;
    return jmatch(pat, javaTok(s), matches);
  }

  public static boolean jmatch(String pat, List<String> toks) {
    return jmatch(pat, toks, null);
  }

  public static 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;
    }
  }

  public static List<String> allToString(Collection c) {
    List<String> l = new ArrayList();
    for (Object o : unnull(c)) l.add(str(o));
    return l;
  }

  public static List<String> allToString(Object[] c) {
    List<String> l = new ArrayList();
    for (Object o : unnull(c)) l.add(str(o));
    return l;
  }

  public static int stdcompare(Number a, Number b) {
    return cmp(a, b);
  }

  public static int stdcompare(String a, String b) {
    return cmp(a, b);
  }

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

  public static int stdcompare(Object a, Object b) {
    return cmp(a, b);
  }

  public static String structure_addTokenMarkers(String s) {
    return join(structure_addTokenMarkers(javaTok(s)));
  }

  public static List<String> structure_addTokenMarkers(List<String> tok) {
    TreeSet<Integer> refs = new TreeSet();
    for (int i = 1; i < l(tok); i += 2) {
      String t = tok.get(i);
      if (t.startsWith("t") && isInteger(t.substring(1)))
        refs.add(parseInt(t.substring(1)));
    }
    if (empty(refs))
      return tok;
    for (int i : refs) {
      int idx = i * 2 + 1;
      String t = "";
      if (endsWithLetterOrDigit(tok.get(idx - 1)))
        t = " ";
      tok.set(idx, t + "m" + i + " " + tok.get(idx));
    }
    return tok;
  }

  public static String jreplace(String s, String in, String out) {
    return jreplace(s, in, out, null);
  }

  public static String jreplace(String s, String in, String out, Object condition) {
    List<String> tok = javaTok(s);
    return jreplace(tok, in, out, condition) ? join(tok) : s;
  }

  public static boolean jreplace(List<String> tok, String in, String out) {
    return jreplace(tok, in, out, false, true, null);
  }

  public static boolean jreplace(List<String> tok, String in, String out, Object condition) {
    return jreplace(tok, in, out, false, true, condition);
  }

  public static 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;
    for (int n = 0; n < 10000; n++) {
      int i = findCodeTokens(tok, 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);
      anyChange = true;
    }
    throw fail("woot? 10000! " + quote(in) + " => " + quote(out));
  }

  public static boolean jreplace_debug;

  public static boolean isCIMap(Map m) {
    return m instanceof TreeMap && ((TreeMap) m).comparator() == caseInsensitiveComparator();
  }

  public static Object safeUnstructure(String s) {
    return unstructure(s, true);
  }

  public static <A> boolean canPutStrictlyIC(Map<A, String> map, A key, String value) {
    if (!(map != null && key != null && value != null))
      return true;
    String old = map.get(key);
    return old == null || eqic(old, value);
  }

  public static Map similarEmptyMap(Map m) {
    if (m instanceof TreeMap)
      return new TreeMap(((TreeMap) m).comparator());
    if (m instanceof LinkedHashMap)
      return new LinkedHashMap();
    return new HashMap();
  }

  public static <A> List<Integer> indicesOfSubList(List<A> x, List<A> y) {
    List<Integer> l = new ArrayList();
    int i = 0;
    while (true) {
      int j = indexOfSubList(x, y, i);
      if (j < 0)
        break;
      l.add(j);
      i = j + 1;
    }
    return l;
  }

  public static boolean strictPutICIfKeyNotEqualToValue(Map<String, String> map, String key, String value) {
    if (eqic(key, value))
      return true;
    return strictPutIC(map, key, value);
  }

  public static <A> boolean addMapToMapWithoutOverwritingIC(Map<A, String> map, Map<A, String> adding) {
    if (!canPutAllStrictlyIC(map, adding))
      return false;
    {
      final Map<A, String> __220 = unnull(adding);
      for (A key : keys(__220)) {
        String value = __220.get(key);
        strictPutIC(map, key, value);
      }
    }
    return true;
  }

  public static int identityHashCode(Object o) {
    return System.identityHashCode(o);
  }

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

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

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

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

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

  public static void lock(Lock lock) {
    try {
      ping();
      if (lock == null)
        return;
      try {
        lock.lockInterruptibly();
      } catch (InterruptedException e) {
        print("Locking interrupted! I probably deadlocked, oops.");
        printStackTrace(e);
        rethrow(e);
      }
      ping();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static void lock(Lock lock, String msg) {
    print("Locking: " + msg);
    lock(lock);
  }

  public static void lock(Lock lock, String msg, long timeout) {
    print("Locking: " + msg);
    lockOrFail(lock, timeout);
  }

  public static ReentrantLock lock() {
    return fairLock();
  }

  public static String mechList_raw_fresh(String name) {
    return mechList_opt_raw_fresh(name);
  }

  public static void unlock(Lock lock, String msg) {
    if (lock == null)
      return;
    print("Unlocking: " + msg);
    lock.unlock();
  }

  public static void unlock(Lock lock) {
    if (lock == null)
      return;
    lock.unlock();
  }

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

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

  public static 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);
    }
  }

  public static HashMap<Set<String>, Class> loadFunctions_cache = new HashMap();

  public static Lock loadFunctions_cached_lock = lock();

  public static boolean loadFunctions_preferDiskCache;

  public static Class loadFunctions_cached(String... functions) {
    return loadFunctions_cached(asList(functions));
  }

  public static Class loadFunctions_cached(Collection<String> functions) {
    TreeSet<String> functionSet = new TreeSet<String>(functions);
    Lock __2150 = loadFunctions_cached_lock;
    lock(__2150);
    try {
      Class c = loadFunctions_cache.get(functionSet);
      if (c != null)
        return c;
      String cached = getTranspiledStandardFunctionsIfCached(functionSet);
      String transpilation = null;
      if (cached != null && (loadFunctions_preferDiskCache || standardFunctionsCacheUpToDate(functionSet)))
        transpilation = cached;
      if (transpilation != null) {
        print("Loading " + joinWithPlus(functionSet));
        c = veryQuickJava_finish(transpilation, emptyList());
      }
      if (c == null)
        c = loadFunctions(functionSet);
      loadFunctions_cache.put(functionSet, c);
      assertNotNull(loadFunctions_cache.get(functionSet));
      return c;
    } finally {
      unlock(__2150);
    }
  }

  public static void loadFunctions_clearCache() {
    Lock __2151 = loadFunctions_cached_lock;
    lock(__2151);
    try {
      loadFunctions_cache.clear();
    } finally {
      unlock(__2151);
    }
  }

  public static void _onLoad_loadFunctions_cached() {
    ownResource(vmBus_onMessage("refreshedTranspiler", new Runnable() {

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

      public String toString() {
        return "loadFunctions_clearCache();";
      }
    }));
  }

  public static boolean preciseCall_debug;

  public static Object preciseCall(Object o, String method, Object... args) {
    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(m, args);
            if (score < Integer.MAX_VALUE) {
              if (preciseCall_debug)
                print("Method score: " + m + " " + score);
              best.put(m, score);
            }
          }
        Method m = best.get();
        if (m != null)
          return invokeMethod(m, null, args);
        return call_withVarargs(((Class) o), method, args);
      } else
        throw todo();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

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

  public static 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);
    throw fail("woot not int: " + getClassName(o));
  }

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

  public static 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);
    }
  }

  public static void _registerIO(Object object, String path, boolean opened) {
  }

  public static <A> A assertEquals(Object x, A y) {
    return assertEquals(null, x, y);
  }

  public static <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;
  }

  public static 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('"');
  }

  public static 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 + "'";
  }

  public static boolean isCISet_gen(Iterable<String> l) {
    return l instanceof TreeSet && className(((TreeSet) l).comparator()).contains("CIComp");
  }

  public static boolean isCIMap_gen(Map map) {
    return map instanceof TreeMap && className(((TreeMap) map).comparator()).contains("CIComp");
  }

  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();
  }

  public static String boolArrayToHex(boolean[] a) {
    return bytesToHex(boolArrayToBytes(a));
  }

  public static String shortDynamicClassName(Object o) {
    if (o instanceof DynamicObject && ((DynamicObject) o).className != null)
      return ((DynamicObject) o).className;
    return shortClassName(o);
  }

  public static Map<Class, Field[]> getDeclaredFields_cache = newDangerousWeakHashMap();

  public static 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) f.setAccessible(true);
      }
    }
    return fields;
  }

  public static boolean startsWithDigit(String s) {
    return nempty(s) && isDigit(s.charAt(0));
  }

  public static String renderFileInfo(File f) {
    return f == null ? "-" : f2s(f) + " " + (f.isFile() ? "(file, " + n2(fileSize(f)) + " bytes)" : f.isDirectory() ? "(dir)" : "(not found)");
  }

  public static String md5(String text) {
    try {
      if (text == null)
        return "-";
      return bytesToHex(md5_impl(toUtf8(text)));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static String md5(byte[] data) {
    if (data == null)
      return "-";
    return bytesToHex(md5_impl(data));
  }

  public static MessageDigest md5_md;

  public static byte[] md5_impl(byte[] data) {
    try {
      return MessageDigest.getInstance("MD5").digest(data);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static String md5(File file) {
    return md5OfFile(file);
  }

  public static String fileNameEncode_safeChars = " ()[]#,!";

  public static String fileNameEncode(String s) {
    StringBuilder buf = new StringBuilder();
    int n = l(s);
    for (int i = 0; i < n; i++) {
      char c = s.charAt(i);
      if (contains(fileNameEncode_safeChars, c))
        buf.append(c);
      else
        buf.append(urlencode(str(c)));
    }
    return str(buf);
  }

  public static List<WeakReference<Class>> hotwire_classes = synchroList();

  public static Class<?> hotwireDependent(String src) {
    Class c = hotwire(src);
    makeDependent(c);
    return c;
  }

  public static Map<JFrame, Boolean> myFrames_list = weakHashMap();

  public static List<JFrame> myFrames() {
    return swing(new F0<List<JFrame>>() {

      public List<JFrame> get() {
        try {
          return keysList(myFrames_list);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "ret keysList(myFrames_list);";
      }
    });
  }

  public static List<String> javaTokNoQuotes(String s) {
    return quotelessJavaTok(s);
  }

  public static List<String> javaTokNoQuotesWithBrackets(String s) {
    return tok_combineRoundOrCurlyBrackets_keep(javaTokNoQuotes(s));
  }

  public static List<String> javaTokNPunctuation_bracketsInC(String s) {
    List<String> tok = javaTok(s);
    for (int i = 1; i < l(tok); i += 2) {
      int j = i;
      String t;
      char c;
      while (j < l(tok) && l(t = tok.get(j)) == 1 && !Character.isLetterOrDigit(c = t.charAt(0)) && "{}()".indexOf(c) < 0) j += 2;
      if (j > i)
        replaceSubList(tok, i - 1, j, ll(joinSubList(tok, i - 1, j)));
    }
    return tok;
  }

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

  public static int charDiff(String a, char b) {
    return charDiff(stringToChar(a), b);
  }

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

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

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

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

  public static <A> A _registerIOWrap(A wrapper, Object wrapped) {
    return wrapper;
  }

  public static Producer<String> javaTokC_noMLS_iterator(final String s) {
    return javaTokC_noMLS_iterator(s, 0);
  }

  public static Producer<String> javaTokC_noMLS_iterator(final String s, final int startIndex) {
    return new Producer<String>() {

      public final 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)) || "'".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
          ++j;
        String t = quickSubstring(s, i, j);
        i = j;
        return t;
      }
    };
  }

  public static 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;
  }

  public static 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;
  }

  public static String internIfLongerThan(String s, int l) {
    return s == null ? null : l(s) >= l ? intern(s) : s;
  }

  public static char unquoteCharacter(String s) {
    assertTrue(s.startsWith("'") && s.length() > 1);
    return unquote("\"" + s.substring(1, s.endsWith("'") ? s.length() - 1 : s.length()) + "\"").charAt(0);
  }

  public static float parseFloat(String s) {
    return Float.parseFloat(s);
  }

  public static SortedMap synchronizedTreeMap() {
    return synchroTreeMap();
  }

  public static List parseList(String s) {
    return (List) safeUnstructure(s);
  }

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

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

  public static 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;
  }

  public static <A> A nuStubInnerObject(Class<A> c) {
    return nuStubInnerObject(c, null);
  }

  public static <A> A nuStubInnerObject(Class<A> c, Object classFinder) {
    try {
      Class outerType = getOuterClass(c, classFinder);
      Constructor m = c.getDeclaredConstructor(outerType);
      m.setAccessible(true);
      return (A) m.newInstance(new Object[] { null });
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static HashMap<String, Class> findClass_cache = new HashMap();

  public static 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;
    }
  }

  public static void setOptAllDyn(DynamicObject o, Map<String, Object> fields) {
    if (fields == null)
      return;
    HashMap<String, Field> fieldMap = instanceFieldsMap(o);
    for (Map.Entry<String, Object> e : fields.entrySet()) {
      String field = e.getKey();
      Object val = e.getValue();
      boolean has = fieldMap.containsKey(field);
      if (has)
        setOpt(o, field, val);
      else {
        o.fieldValues.put(intern(field), val);
      }
    }
  }

  public static 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));
    }
  }

  public static 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));
      }
    }
  }

  public static String intern(String s) {
    return fastIntern(s);
  }

  public static void pcallOpt_noArgs(Object o, String method) {
    try {
      callOpt_noArgs(o, method);
    } catch (Throwable __e) {
      _handleException(__e);
    }
  }

  public static int[] toIntArray(List<Integer> l) {
    int[] a = new int[l(l)];
    for (int i = 0; i < a.length; i++) a[i] = l.get(i);
    return a;
  }

  public static 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);";
      }
    });
  }

  public static 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);";
      }
    });
  }

  public static Runnable _topLevelErrorHandling(final Runnable runnable) {
    final Object info = _threadInfo();
    return new Runnable() {

      public void run() {
        try {
          try {
            _threadInheritInfo(info);
            runnable.run();
          } catch (Throwable __e) {
            _handleException(__e);
          }
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "pcall {\r\n      _threadInheritInfo(info);\r\n      runnable.run();\r\n    }";
      }
    };
  }

  public static void consoleClearInput() {
    consoleSetInput("");
  }

  public static Random defaultRandomGenerator() {
    return ThreadLocalRandom.current();
  }

  public static Lock downloadLock_lock = fairLock();

  public static Lock downloadLock() {
    return downloadLock_lock;
  }

  public static String getSnippetTitleOpt(String s) {
    return isSnippetID(s) ? getSnippetTitle(s) : s;
  }

  public static 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();
  }

  public static List<String> mechList_opt_tlft(String name) {
    return mechList_opt_tlft_cached(name);
  }

  public static List<String> splitAtDoubleArrow_bothDirections(String s) {
    return splitAtDoubleArrow_bothDirections(javaTok(s));
  }

  public static List<String> splitAtDoubleArrow_bothDirections(List<String> tok) {
    List<String> l = splitAtDoubleArrow(tok);
    if (l(l) != 2)
      l = reversedList(splitAtDoubleLeftArrow(tok));
    return l;
  }

  public static List<String> trimAll(Collection<String> l) {
    List<String> l2 = new ArrayList();
    if (l != null)
      for (String s : l) l2.add(trim(s));
    return l2;
  }

  public static boolean shortClassNameIs(Object o, String name) {
    return eq(shortClassName(o), name);
  }

  public static Object callCreator(String functionName, Object... args) {
    return call(creator(), functionName, args);
  }

  public static <A> Set<A> synchroWeakHashSet() {
    return Collections.newSetFromMap((Map) newWeakHashMap());
  }

  public static <A> boolean setAdd(Collection<A> c, A a) {
    if (c == null || c.contains(a))
      return false;
    c.add(a);
    return true;
  }

  public static void cancelThread(Thread t) {
    if (t == null)
      return;
    ping();
    synchronized (ping_actions) {
      ping_actions.put(t, "cancelled");
      ping_anyActions = true;
    }
  }

  public static boolean standardFunctionAlwaysReturnsVoid(String name) {
    return tok_staticFunctionAlwaysReturnsVoid(javaTok(textOfStandardFunction_cached(name)), name);
  }

  public static void tokPrepend(List<String> tok, int i, String s) {
    tok.set(i, s + tok.get(i));
  }

  public static boolean transpileRaw_silent = true;

  public static boolean transpileRaw_useDiskCache;

  public static String transpileRaw(String mainJava) {
    return (String) call(dm_transpiler(), "transpileRaw", mainJava, false, transpileRaw_useDiskCache);
  }

  public static Class veryQuickJava_finish(String src, List<String> libs) {
    libs = cloneList(libs);
    src = findTranslators2(src, libs);
    String dehlibs = join(" ", libs);
    File bytecode = null;
    try {
      bytecode = javaCompile_overInternalBot(src, dehlibs);
    } catch (Throwable _e) {
      print("Was compiling: " + src + "\n");
      throw rethrow(_e);
    }
    return hotwireCore(concatLists(ll(bytecode), loadLibraries(libs)));
  }

  public static <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;
    }
  }

  public static List<String> dropPunctuation_keep = litlist("*", "<", ">");

  public static 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;
  }

  public static String dropPunctuation(String s) {
    return join(dropPunctuation(nlTok(s)));
  }

  public static List<String> javaTokPlusPeriod(String s) {
    List<String> tok = new ArrayList<String>();
    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;
  }

  public static boolean endsWithIgnoreCase(String a, String b) {
    int la = l(a), lb = l(b);
    return la >= lb && regionMatchesIC(a, la - lb, b, 0, lb);
  }

  public static boolean endsWithIgnoreCase(String a, String b, Matches m) {
    if (!endsWithIgnoreCase(a, b))
      return false;
    m.m = new String[] { substring(a, 0, l(a) - l(b)) };
    return true;
  }

  public static BufferedReader bufferedReader(Reader r) {
    return r instanceof BufferedReader ? (BufferedReader) r : _registerIOWrap(new BufferedReader(r), r);
  }

  public static <A> CloseableIterableIterator<A> iteratorFromFunction_f0_autoCloseable(final F0<A> f, final AutoCloseable closeable) {
    class IFF2 extends CloseableIterableIterator<A> {

      public A a;

      public boolean done;

      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();
  }

  public static String readLineFromReaderWithClose(BufferedReader r) {
    try {
      String s = r.readLine();
      if (s == null)
        r.close();
      return s;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static 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);
      }
    };
  }

  public static FileInputStream newFileInputStream(File path) throws IOException {
    return newFileInputStream(path.getPath());
  }

  public static FileInputStream newFileInputStream(String path) throws IOException {
    FileInputStream f = new FileInputStream(path);
    _registerIO(f, path, true);
    return f;
  }

  public static File standardLogFile() {
    return getProgramFile("log");
  }

  public static void logQuoted(String logFile, String line) {
    logQuoted(getProgramFile(logFile), line);
  }

  public static void logQuoted(File logFile, String line) {
    appendToFile(logFile, quote(line) + "\n");
  }

  public static <A extends Component> A _recordNewSwingComponent(A c) {
    if (c != null)
      callF((Object) vm_generalMap_get("newSwingComponentRegistry"), (Object) c);
    return c;
  }

  public static JLabel jlabel(final String text) {
    return swingConstruct(BetterLabel.class, text);
  }

  public static JLabel jlabel() {
    return jlabel(" ");
  }

  public static void enableWordWrapForTextArea(final JTextArea ta) {
    if (ta != null) {
      swing(new Runnable() {

        public void run() {
          try {
            ta.setLineWrap(true);
            ta.setWrapStyleWord(true);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "ta.setLineWrap(true);\r\n    ta.setWrapStyleWord(true);";
        }
      });
    }
  }

  public static JTextArea jtextarea() {
    return jTextArea();
  }

  public static JTextArea jtextarea(String text) {
    return jTextArea(text);
  }

  public static boolean setText_opt = true;

  public static <A extends JTextComponent> A setText(A c, Object text) {
    setText((Object) c, text);
    return c;
  }

  public static <A extends JComboBox> A setText(final A c, Object text) {
    final String s = strUnnull(text);
    {
      swing(new Runnable() {

        public void run() {
          try {
            c.getEditor().setItem(s);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "c.getEditor().setItem(s);";
        }
      });
    }
    return c;
  }

  public static void setText(JLabel c, Object text) {
    setText((Object) c, text);
  }

  public static JButton setText(JButton c, Object text) {
    setText((Object) c, jlabel_textAsHTML_center_ifNeeded(strUnnull(text)));
    return c;
  }

  public static <A> A setText(final A c, Object text) {
    if (c == null)
      return null;
    final String s = strUnnull(text);
    {
      swing(new Runnable() {

        public void run() {
          try {
            if (!setText_opt || neq(callOpt(c, "getText"), s))
              call(c, "setText", s);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "if (!setText_opt || neq(callOpt(c, \"getText\"), s))\r\n      call(c, \"setText\", s);";
        }
      });
    }
    return c;
  }

  public static JTextField onEnter(final JTextField tf, final Object action) {
    if (action == null || tf == null)
      return tf;
    tf.addActionListener(new java.awt.event.ActionListener() {

      public void actionPerformed(java.awt.event.ActionEvent _evt) {
        try {
          tf.selectAll();
          callF(action);
        } catch (Throwable __e) {
          messageBox(__e);
        }
      }
    });
    return tf;
  }

  public static JButton onEnter(JButton btn, final Object action) {
    if (action == null || btn == null)
      return btn;
    btn.addActionListener(actionListener(action));
    return btn;
  }

  public static JList onEnter(JList list, Object action) {
    list.addKeyListener(enterKeyListener(rCallOnSelectedListItem(list, action)));
    return list;
  }

  public static JComboBox onEnter(final JComboBox cb, final Object action) {
    {
      swing(new Runnable() {

        public void run() {
          try {
            if (cb.isEditable()) {
              JTextField text = (JTextField) cb.getEditor().getEditorComponent();
              onEnter(text, action);
            } else {
              cb.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "enter");
              cb.getActionMap().put("enter", abstractAction("", new Runnable() {

                public void run() {
                  try {
                    cb.hidePopup();
                    callF(action);
                  } catch (Exception __e) {
                    throw rethrow(__e);
                  }
                }

                public String toString() {
                  return "cb.hidePopup(); callF(action);";
                }
              }));
            }
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "if (cb.isEditable()) {\r\n      JTextField text = (JTextField) cb.getEditor().g...";
        }
      });
    }
    return cb;
  }

  public static JTable onEnter(final JTable table, final Object action) {
    table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "Enter");
    table.getActionMap().put("Enter", new AbstractAction() {

      public void actionPerformed(ActionEvent e) {
        callF(action, table.getSelectedRow());
      }
    });
    return table;
  }

  public static JTextField onEnter(Object action, JTextField tf) {
    return onEnter(tf, action);
  }

  public static <A> A heldInstance(Class<A> c) {
    List<Object> l = holdInstance_l.get();
    for (int i = l(l) - 1; i >= 0; i--) {
      Object o = l.get(i);
      if (isInstanceOf(o, c))
        return (A) o;
    }
    throw fail("No instance of " + className(c) + " held");
  }

  public static float getSwingFontScale() {
    return or((Float) vm_generalMap_get("swingFontScale_value"), 1f);
  }

  public static ActionListener actionListener(final Object runnable) {
    return actionListener(runnable, null);
  }

  public static ActionListener actionListener(final Object runnable, final Object instanceToHold) {
    if (runnable instanceof ActionListener)
      return (ActionListener) runnable;
    final Object info = _threadInfo();
    return new java.awt.event.ActionListener() {

      public void actionPerformed(java.awt.event.ActionEvent _evt) {
        try {
          _threadInheritInfo(info);
          AutoCloseable __2129 = holdInstance(instanceToHold);
          try {
            callF(runnable);
          } finally {
            _close(__2129);
          }
        } catch (Throwable __e) {
          messageBox(__e);
        }
      }
    };
  }

  public static <A> List<A> reverseList(List<A> l) {
    Collections.reverse(l);
    return l;
  }

  public static boolean structContainsAnonymousClasses(String s) {
    Pattern p = compileRegexp("\\$\\d");
    Producer<String> it = javaTokC_noMLS_iterator(s);
    String t;
    while ((t = it.next()) != null) if (isIdentifier(s) && regexpContains(p, t))
      return true;
    return false;
  }

  public static Object unstruct(String text) {
    return unstructure(text);
  }

  public static String format(String pat, Object... args) {
    return format3(pat, args);
  }

  public static List<String> matchAny_firstGroups(String pat, Iterable<String> l) {
    Matches m = new Matches();
    List<String> out = new ArrayList();
    for (String s : unnull(l)) if (match(pat, s, m))
      out.add(m.unq(0));
    return out;
  }

  public static List<String> matchX_any_firstGroups(String pat, Iterable<String> l) {
    Matches m = new Matches();
    List<String> out = new ArrayList();
    for (String s : unnull(l)) if (matchX(pat, s, m))
      out.add(m.unq(0));
    return out;
  }

  public static <A> Set<A> setIntersection(Set<A> a, Collection<A> b) {
    Set<A> set = similarEmptySet(a);
    Set<A> bSet = asSet(b);
    for (A x : a) if (bSet.contains(x))
      set.add(x);
    return set;
  }

  public static <A, B> MultiMap<A, B> uniquifyMultiMapValues(MultiMap<A, B> mm) {
    return mm == null ? null : new MultiMap(mapValues("uniquify", mm.data));
  }

  public static <A, B> MultiMap<A, B> pairsToMultiMap(List<Pair<A, B>> l) {
    MultiMap<A, B> mm = new MultiMap();
    for (Pair<A, B> p : unnull(l)) mm.put(p.a, p.b);
    return mm;
  }

  public static List<Pair<String, String>> matchX_any_firstAndSecondGroups(String pat, Iterable<String> l) {
    Matches m = new Matches();
    List<Pair<String, String>> out = new ArrayList();
    for (String s : unnull(l)) if (matchX(pat, s, m))
      out.add(pair(m.unq(0), m.unq(1)));
    return out;
  }

  public static <A, B> MultiMap<A, B> multiMapDiff(MultiMap<A, B> mm1, MultiMap<A, B> mm2) {
    return pairsToMultiMap(listMinusSet(multiMapToPairs(mm1), multiMapToPairs(mm2)));
  }

  public static Object safeUnstruct(String s) {
    return unstructure(s, true);
  }

  public static <A, B extends A> Set<A> setDiff(Set<A> l, Collection<B> stuff) {
    return setMinusSet(l, stuff);
  }

  public static <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);
    }
  }

  public static <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);
    }
  }

  public static <A, B> B getOrCreate(Class<? extends B> c, Map<A, B> map, A key) {
    return getOrCreate(map, key, c);
  }

  public static <A> List<A> dropFirstThreeAndLastThree(List<A> l) {
    return dropFirstAndLast(3, l);
  }

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

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

  public 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);
  }

  public static void lockOrFail(Lock lock, long timeout) {
    try {
      ping();
      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);
      }
      ping();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static ReentrantLock fairLock() {
    return new ReentrantLock(true);
  }

  public static String mechList_opt_raw_fresh(String name) {
    Object readMode = mechMode().readMode;
    if (readMode instanceof File) {
      String md5Name = uniqueFileNameUsingMD5_80_v2(upper(name));
      return loadTextFileFromZipFile((File) readMode, md5Name);
    }
    boolean useLocal = false, useRemote = true;
    if (eq(readMode, "mergeLocalAndRemote"))
      useLocal = true;
    else if (eq(readMode, "local")) {
      useLocal = true;
      useRemote = false;
    }
    String s = "";
    if (useRemote) {
      if (eq(mechMode().readMode, "localCopies"))
        s = unnull(loadTextFile(remoteMechListMirrorFile(name)));
      else
        s = serverMechList_raw_fresh(name, true);
    }
    if (useLocal)
      s = appendNewLineIfNempty(s) + localMechList_opt_raw_fresh(name);
    return s;
  }

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

  public static 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;
  }

  public static String getTranspiledStandardFunctionsIfCached(TreeSet<String> functionSet) {
    try {
      File f = transpiledStandardFunctions_file(functionSet);
      return loadTextFile(f);
    } catch (Throwable __e) {
      return null;
    }
  }

  public static boolean standardFunctionsCacheUpToDate(TreeSet<String> functionSet) {
    File f = transpiledStandardFunctions_file(functionSet);
    long date = recommendedTranspilationDateForStandardFunctions(functionSet);
    return date != 0 && f.lastModified() >= date;
  }

  public static String joinWithPlus(Collection<String> c) {
    return join("+", c);
  }

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

  public static boolean loadFunctions_debug;

  public static Object loadFunctions_veryQuickJava;

  public static Class loadFunctions(String... functions) {
    return loadFunctions(asList(functions));
  }

  public static Class loadFunctions(Collection<String> functions) {
    print("Compiling " + joinWithPlus(functions));
    TreeSet<String> functionSet = new TreeSet<String>(functions);
    StringBuilder buf = new StringBuilder();
    for (String s : functionSet) buf.append("please include function " + assertIdentifier(s) + ".\n");
    long time = sysNow();
    Class c;
    if (loadFunctions_veryQuickJava != null)
      c = (Class) callF(loadFunctions_veryQuickJava, str(buf));
    else {
      veryQuickJava_transpiled.set("");
      c = veryQuickJava(str(buf));
      saveTranspiledStandardFunctions(functionSet, getAndClearThreadLocal(veryQuickJava_transpiled));
    }
    if (loadFunctions_debug)
      done2_always(time, "loadFunctions: " + join(" ", functions));
    return c;
  }

  public static <A> AutoCloseable vmBus_onMessage(final String msg, final VF1<A> onMessage) {
    Map<String, Set> map = vm_busListenersByMessage_live();
    synchronized (map) {
      Set listeners = map.get(msg);
      if (listeners == null)
        map.put(msg, listeners = syncIdentityHashSet());
      return tempAdd(listeners, new VF2<String, Object>() {

        public void get(String _msg, Object arg) {
          try {
            callF(onMessage, arg);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "callF(onMessage, arg)";
        }
      });
    }
  }

  public static <A, B> AutoCloseable vmBus_onMessage(String msg, final VF2<A, B> onMessage) {
    return vmBus_onMessage(msg, new VF1<Object[]>() {

      public void get(Object[] o) {
        try {
          callF(onMessage, first(o), second(o));
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "callF(onMessage, first(o), second(o));";
      }
    });
  }

  public static AutoCloseable vmBus_onMessage(String msg, Runnable onMessage) {
    return vmBus_onMessage(msg, runnableToVF1(onMessage));
  }

  public static int methodApplicabilityScore(Method m, Object[] args) {
    Class<?>[] types = m.getParameterTypes();
    if (types.length != args.length)
      return Integer.MAX_VALUE;
    int score = 0;
    for (int i = 0; i < types.length; i++) {
      Object a = args[i];
      Class c = types[i];
      if (a == null)
        ++score;
      else if (c == a.getClass()) {
      } else if (isInstanceX(c, a))
        ++score;
      else
        return Integer.MAX_VALUE;
    }
    return score;
  }

  public static RuntimeException todo() {
    throw new RuntimeException("TODO");
  }

  public static RuntimeException todo(String msg) {
    throw new RuntimeException("TODO: " + msg);
  }

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

  public static void assertVerbose(boolean b) {
    assertVerbose_value.set(b);
  }

  public static boolean assertVerbose() {
    return isTrue(assertVerbose_value.get());
  }

  public static <A> A assertEqualsVerbose(Object x, A y) {
    assertEqualsVerbose((String) null, x, y);
    return y;
  }

  public static <A> A assertEqualsVerbose(String msg, Object x, A y) {
    if (!eq(x, y)) {
      throw fail((msg != null ? msg + ": " : "") + (y) + " != " + (x));
    } else
      print("OK: " + (x));
    return y;
  }

  public static 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;
  }

  public static 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);
  }

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

  public static long fileSize(String path) {
    return getFileSize(path);
  }

  public static long fileSize(File f) {
    return getFileSize(f);
  }

  public static byte[] toUtf8(String s) {
    try {
      return s.getBytes("UTF-8");
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static boolean md5OfFile_verbose;

  public static String md5OfFile(String path) {
    return md5OfFile(newFile(path));
  }

  public static 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);
    }
  }

  public static String urlencode(String x) {
    try {
      return URLEncoder.encode(unnull(x), "UTF-8");
    } catch (UnsupportedEncodingException e) {
      throw new RuntimeException(e);
    }
  }

  public static ThreadLocal<List<Object>> holdInstance_l = new ThreadLocal();

  public static AutoCloseable holdInstance(Object o) {
    if (o == null)
      return null;
    listThreadLocalAdd(holdInstance_l, o);
    return new AutoCloseable() {

      public void close() {
        listThreadLocalPopLast(holdInstance_l);
      }
    };
  }

  public static Class<?> hotwire(String src) {
    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;
    }
  }

  public static <A, B> Map<A, B> weakHashMap() {
    return newWeakHashMap();
  }

  public static <A, B> List<A> keysList(Map<A, B> map) {
    return cloneListSynchronizingOn(keys(map), map);
  }

  public static <A> List<A> keysList(MultiSet<A> ms) {
    return ms == null ? null : keysList(ms.map);
  }

  public static List<String> quotelessJavaTok(String s) {
    ArrayList<String> tok = 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;
      }
      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 (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
        ++j;
      tok.add(javaTok_substringC(s, i, j));
      ++n;
      i = j;
    }
    if ((tok.size() % 2) == 0)
      tok.add("");
    return tok;
  }

  public static List<String> replaceSubList(List<String> l, List<String> x, List<String> y) {
    return replaceSublist(l, x, y);
  }

  public static <A> List<A> replaceSubList(List<A> l, int fromIndex, int toIndex, List<A> y) {
    return replaceSublist(l, fromIndex, toIndex, y);
  }

  public static char stringToChar(String s) {
    if (l(s) != 1)
      throw fail("bad stringToChar: " + s);
    return firstChar(s);
  }

  public static String quickSubstring(String s, int i, int j) {
    if (i == j)
      return "";
    return s.substring(i, j);
  }

  public static NavigableMap synchroTreeMap() {
    return Collections.synchronizedNavigableMap(new TreeMap());
  }

  public static Class getOuterClass(Class c) {
    return getOuterClass(c, null);
  }

  public static Class getOuterClass(Class c, Object classFinder) {
    try {
      String s = c.getName();
      int i = s.lastIndexOf('$');
      String name = substring(s, 0, i);
      if (classFinder != null)
        return (Class) callF(classFinder, name);
      return Class.forName(name);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

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

  public static void warnIfOddCount(Object... list) {
    if (odd(l(list)))
      printStackTrace("Odd list size: " + list);
  }

  public static Method fastIntern_method;

  public static 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);
    }
  }

  public static Map<Class, HashMap<String, Method>> callOpt_noArgs_cache = newDangerousWeakHashMap();

  public static 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);
    }
  }

  public static 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) {
        m.setAccessible(true);
        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;
  }

  public static Class hotwireOnce(String programID) {
    return hotwireCached(programID, false);
  }

  public static void consoleSetInput(final String text) {
    if (headless())
      return;
    setTextAndSelectAll(consoleInputField(), text);
    focusConsole();
  }

  public static String getSnippetTitle(String id) {
    try {
      if (id == null)
        return null;
      if (!isSnippetID(id))
        return "?";
      IResourceLoader rl = vm_getResourceLoader();
      if (rl != null)
        return rl.getSnippetTitle(id);
      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);
    }
  }

  public static String getSnippetTitle(long id) {
    return getSnippetTitle(fsI(id));
  }

  public static Map<String, MechListDependentCache<List<String>>> mechList_opt_tlft_cached_cache = ciMap();

  public static Lock mechList_opt_tlft_cached_lock = lock();

  public static List<String> mechList_opt_tlft_cached(String name) {
    MechListDependentCache<List<String>> listCache;
    {
      Lock __2056 = mechList_opt_tlft_cached_lock;
      lock(__2056);
      try {
        listCache = mechList_opt_tlft_cached_cache.get(name);
        if (listCache == null)
          mechList_opt_tlft_cached_cache.put(name, listCache = new MechListDependentCache(name, new F1<String, List<String>>() {

            public List<String> get(String x) {
              try {
                return tlft(x);
              } catch (Exception __e) {
                throw rethrow(__e);
              }
            }

            public String toString() {
              return "tlft(x)";
            }
          }));
      } finally {
        unlock(__2056);
      }
    }
    return listCache.get();
  }

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

  public static List<String> splitAtDoubleLeftArrow(String s) {
    return splitAtDoubleLeftArrow(javaTok(s));
  }

  public static List<String> splitAtDoubleLeftArrow(List<String> tok) {
    List<String> l = new ArrayList();
    int i = 0;
    while (i < l(tok)) {
      int j = indexOfSubList(tok, ll("<", "", "="), i);
      if (i >= l(tok))
        break;
      if (j < 0)
        j = l(tok);
      l.add(trimJoin(tok.subList(i, j)));
      i = j + 3;
    }
    return l;
  }

  public static boolean tok_staticFunctionAlwaysReturnsVoid(List<String> tok, String name) {
    Set<String> types = tok_returnTypesOfStaticFunction_uncleaned(tok, name);
    if (empty(types))
      return false;
    for (String type : types) if (!containsOneOf(javaTokC(type), "void", "svoid"))
      return false;
    return true;
  }

  public static String textOfStandardFunction_cached(String sfName) {
    return textOfStandardFunction(sfName);
  }

  public static Object dm_transpiler() {
    return dm_getModule(dm_requireModule("#1017065/Transpiler"));
  }

  public static String findTranslators2(String src, List<String> libsOut) {
    return join(findTranslators2(javaTok(src), libsOut));
  }

  public static List<String> findTranslators2(List<String> tok, List<String> libsOut) {
    int i;
    while ((i = jfind(tok, "!<int>")) >= 0) {
      setAdd(libsOut, tok.get(i + 2));
      clearTokens(tok, i, i + 3);
    }
    return tok;
  }

  public static File javaCompile_overInternalBot(String src) {
    return javaCompile_overInternalBot(src, "");
  }

  public static synchronized File javaCompile_overInternalBot(String src, String dehlibs) {
    return CompilerBot.compile(src, dehlibs);
  }

  public static Class hotwireCore(List<File> files) {
    try {
      JavaXClassLoader classLoader = hotwire_makeClassLoader(files);
      Class<?> theClass = classLoader.loadClass("main");
      setOpt(theClass, "__javax", getJavaX());
      if (getOpt(theClass, "programID") == null)
        setOpt(theClass, "programID", "#3999999");
      if (!_inCore())
        hotwire_copyOver(theClass);
      return theClass;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static List<File> loadLibraries(List<String> snippetIDs) {
    return map("loadLibrary", snippetIDs);
  }

  public static List<String> nlTok(String s) {
    return javaTokPlusPeriod(s);
  }

  public static Lock appendToFile_lock = lock();

  public static boolean appendToFile_keepOpen;

  public static HashMap<String, Writer> appendToFile_writers = new HashMap();

  public static void appendToFile(String path, String s) {
    try {
      Lock __2200 = appendToFile_lock;
      lock(__2200);
      try {
        mkdirsForFile(new File(path));
        path = getCanonicalPath(path);
        Writer writer = appendToFile_writers.get(path);
        if (writer == null) {
          writer = new BufferedWriter(new OutputStreamWriter(newFileOutputStream(path, true), "UTF-8"));
          if (appendToFile_keepOpen)
            appendToFile_writers.put(path, writer);
        }
        writer.write(s);
        if (!appendToFile_keepOpen)
          writer.close();
      } finally {
        unlock(__2200);
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static void appendToFile(File path, String s) {
    if (path != null)
      appendToFile(path.getPath(), s);
  }

  public static void cleanMeUp_appendToFile() {
    AutoCloseable __2203 = tempCleaningUp();
    try {
      Lock __2202 = appendToFile_lock;
      lock(__2202);
      try {
        closeAllWriters(values(appendToFile_writers));
        appendToFile_writers.clear();
      } finally {
        unlock(__2202);
      }
    } finally {
      _close(__2203);
    }
  }

  public static <A> A swingConstruct(final Class<A> c, final Object... args) {
    return swing(new F0<A>() {

      public A get() {
        try {
          return nuObject(c, args);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "ret nuObject(c, args);";
      }
    });
  }

  public static JTextArea jTextArea() {
    return jTextArea("");
  }

  public static JTextArea jTextArea(final String text) {
    return jTextAreaWithUndo(text);
  }

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

  public static 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);
  }

  public static void messageBox(final String msg) {
    if (headless())
      print(msg);
    else {
      swing(new Runnable() {

        public void run() {
          try {
            JOptionPane.showMessageDialog(null, msg, "JavaX", JOptionPane.INFORMATION_MESSAGE);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "JOptionPane.showMessageDialog(null, msg, \"JavaX\", JOptionPane.INFORMATION_MES...";
        }
      });
    }
  }

  public static void messageBox(Throwable e) {
    printStackTrace(e);
    messageBox(hideCredentials(innerException2(e)));
  }

  public static KeyListener enterKeyListener(final Object action) {
    return new KeyAdapter() {

      public void keyPressed(KeyEvent ke) {
        if (ke.getKeyCode() == KeyEvent.VK_ENTER)
          pcallF(action);
      }
    };
  }

  public static Runnable rCallOnSelectedListItem(final JList list, final Object action) {
    return new Runnable() {

      public void run() {
        try {
          pcallF(action, getSelectedItem(list));
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "pcallF(action, getSelectedItem(list))";
      }
    };
  }

  public static AbstractAction abstractAction(String name, final Object runnable) {
    return new AbstractAction(name) {

      public void actionPerformed(ActionEvent evt) {
        pcallF(runnable);
      }
    };
  }

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

  public static boolean regexpContains(String pat, String s) {
    return regexpFinds(pat, s);
  }

  public static boolean regexpContains(Pattern pat, String s) {
    return regexpFinds(pat, s);
  }

  public static Set similarEmptySet(Collection m) {
    if (m instanceof TreeSet)
      return new TreeSet(((TreeSet) m).comparator());
    if (m instanceof LinkedHashSet)
      return new LinkedHashSet();
    return new HashSet();
  }

  public static Map mapValues(Object func, Map map) {
    Map m = similarEmptyMap(map);
    for (Object key : keys(map)) m.put(key, callF(func, map.get(key)));
    return m;
  }

  public static Map mapValues(Map map, Object func) {
    return mapValues(func, map);
  }

  public static <A> List<A> uniquify(Collection<A> l) {
    return uniquifyList(l);
  }

  public static <A, B> List<Pair<A, B>> multiMapToPairs(MultiMap<A, B> mm) {
    List<Pair<A, B>> out = new ArrayList();
    for (A key : keys(mm)) for (B value : mm.get(key)) out.add(pair(key, value));
    return out;
  }

  public static <A, B extends A> Set<A> setMinusSet(Set<A> l, Collection<B> stuff) {
    if (empty(stuff))
      return l;
    Set<B> set = asSet(stuff);
    Set<A> l2 = similarEmptySet(l);
    for (A a : l) if (!set.contains(a))
      l2.add(a);
    return l2;
  }

  public static <A> List<A> dropFirstAndLast(int n, List<A> l) {
    return new ArrayList(subList(l, n, l(l) - n));
  }

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

  public static String dropFirstAndLast(String s) {
    return substring(s, 1, l(s) - 1);
  }

  public static String loadTextFileFromZipFile(File inZip, String fileName) {
    try {
      if (!fileExists(inZip))
        return null;
      try {
        ZipFile zip = new ZipFile(inZip);
        try {
          return loadTextFileFromZipFile(zip, fileName);
        } finally {
          _close(zip);
        }
      } catch (Throwable e) {
        throw fail(f2s(inZip), e);
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static String loadTextFileFromZipFile(ZipFile zip, String fileName) {
    try {
      ZipEntry entry = zip.getEntry(fileName);
      if (entry == null)
        return null;
      InputStream fin = zip.getInputStream(entry);
      try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        copyStream(fin, baos);
        return fromUTF8(baos.toByteArray());
      } finally {
        _close(fin);
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static File remoteMechListMirrorFile(String listName) {
    return newFile(remoteMechListMirrorsDir(), uniqueFileNameUsingMD5_80_v2(upper(listName)));
  }

  public static boolean serverMechList_raw_fresh_verbose;

  public static String serverMechList_raw_fresh(String name) {
    return serverMechList_raw_fresh(name, false);
  }

  public static String serverMechList_raw_fresh(String name, boolean opt) {
    Lock __2189 = downloadLock();
    lock(__2189);
    try {
      String text = null;
      try {
        text = loadTextFile(remoteMechListMirrorFile(name));
      } catch (Throwable __e) {
        _handleException(__e);
      }
      Object[] params = muricaCredentialsPlus("md5", md5OrNull(text), "l", l(text), "opt", opt ? 1 : 0, "withStatus", 1);
      String url = "http://butter.botcompany.de:8080/mech/raw/list-text/" + urlencode(name);
      String page = postPageSilently(url, params);
      Map map = jsonDecodeMap(page);
      boolean same = eq(map.get("Same"), true);
      boolean appended = eq(map.get("Appended"), true);
      saveTextFile(remoteMechListMirrorMetaFile(name), struct(getMultipleKeys(map, "Name", "Status")));
      if (!same) {
        if (appended)
          text += (String) map.get("Text");
        else
          text = (String) map.get("Text");
        saveTextFile(remoteMechListMirrorFile(name), text);
        File nameFile = remoteMechListMirrorNameFile(name);
        if (!fileExists(nameFile)) {
          String actualName = or((String) map.get("Name"), name);
          saveTextFile(nameFile, actualName);
        }
      }
      if (serverMechList_raw_fresh_verbose)
        print("Mech list " + name + ": " + (appended ? "appended" : same ? "same" : "downloaded") + ": " + n2(countLines(text), "line"));
      if (!same)
        vmBus_send("remoteMechListMirrorChanged", name);
      return text;
    } finally {
      unlock(__2189);
    }
  }

  public static String appendNewLineIfNempty(String s) {
    return empty(s) ? "" : s + "\n";
  }

  public static String localMechList_opt_raw_fresh(String name) {
    return unnull(loadTextFile(localMechListFile(name)));
  }

  public static File transpiledStandardFunctions_file(TreeSet<String> functionSet) {
    String name = uniqueFileNameUsingMD5_80_v2(join("+", functionSet));
    File dir = javaxCachesDir("Transpiled Standard Functions");
    saveTextFileUnlessExists(newFile(dir, name + ".functions"), lines(functionSet));
    return newFile(dir, name + ".java");
  }

  public static long recommendedTranspilationDateForStandardFunctions(Collection<String> sfNames) {
    long date = 0;
    for (String name : sfNames) {
      long d = recommendedTranspilationDateForStandardFunction(name);
      if (d == 0)
        return 0;
      date = max(date, d);
    }
    return date;
  }

  public static void saveTranspiledStandardFunctions(TreeSet<String> functionSet, String transpiledSrc) {
    try {
      saveTextFile(transpiledStandardFunctions_file(functionSet), transpiledSrc);
    } catch (Throwable __e) {
      _handleException(__e);
    }
  }

  public static <A> A third(List<A> l) {
    return _get(l, 2);
  }

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

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

  public static <A> VF1<A> runnableToVF1(final Runnable r) {
    return r == null ? null : new VF1<A>() {

      public void get(A a) {
        try {
          r.run();
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "r.run()";
      }
    };
  }

  public static 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);
  }

  public static 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);
    }
  }

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

  public static HashMap getOpt_special;

  public static Object getOpt_cached(Object o, String field) {
    try {
      if (o == null)
        return null;
      Class c = o.getClass();
      HashMap<String, Field> map;
      synchronized (getOpt_cache) {
        map = getOpt_cache.get(c);
        if (map == null)
          map = getOpt_makeCache(c);
      }
      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 ((DynamicObject) o).fieldValues.get(field);
      return null;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static HashMap<String, Field> getOpt_makeCache(Class c) {
    HashMap<String, Field> map;
    if (isSubtypeOf(c, Map.class))
      map = getOpt_special;
    else {
      map = new HashMap();
      Class _c = c;
      do {
        for (Field f : _c.getDeclaredFields()) {
          f.setAccessible(true);
          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;
  }

  public static File loadLibrary(String snippetID) {
    return loadBinarySnippet(snippetID);
  }

  public static <A> void listThreadLocalAdd(ThreadLocal<List<A>> tl, A a) {
    List<A> l = tl.get();
    if (l == null)
      tl.set(l = new ArrayList());
    l.add(a);
  }

  public static <A> A listThreadLocalPopLast(ThreadLocal<List<A>> tl) {
    List<A> l = tl.get();
    if (l == null)
      return null;
    A a = popLast(l);
    if (empty(l))
      tl.set(null);
    return a;
  }

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

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

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

  public static boolean _inCore() {
    return false;
  }

  public static List hotwire_copyOver_after = synchroList();

  public static 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);
  }

  public static <A> ArrayList<A> cloneListSynchronizingOn(Collection<A> l, Object mutex) {
    if (l == null)
      return new ArrayList();
    synchronized (mutex) {
      return new ArrayList<A>(l);
    }
  }

  public static char firstChar(String s) {
    return s.charAt(0);
  }

  public 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);
  }

  public static Method findMethodNamed(Class c, String method) {
    while (c != null) {
      for (Method m : c.getDeclaredMethods()) if (m.getName().equals(method)) {
        m.setAccessible(true);
        return m;
      }
      c = c.getSuperclass();
    }
    return null;
  }

  public static void upgradeJavaXAndRestart() {
    run("#1001639");
    restart();
    sleep();
  }

  public static TreeMap<String, Class> hotwireCached_cache = new TreeMap();

  public static Lock hotwireCached_lock = lock();

  public static Class hotwireCached(String programID) {
    return hotwireCached(programID, true);
  }

  public static Class hotwireCached(String programID, boolean runMain) {
    return hotwireCached(programID, runMain, false);
  }

  public static Class hotwireCached(String programID, boolean runMain, boolean dependent) {
    Lock __2280 = hotwireCached_lock;
    lock(__2280);
    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(__2280);
    }
  }

  public static boolean headless() {
    return isHeadless();
  }

  public static JTextField setTextAndSelectAll(final JTextField tf, final String text) {
    {
      swing(new Runnable() {

        public void run() {
          try {
            tf.setText(text);
            tf.selectAll();
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "tf.setText(text);\r\n    tf.selectAll();";
        }
      });
    }
    return tf;
  }

  public static JTextField consoleInputField() {
    Object console = get(getJavaX(), "console");
    return (JTextField) getOpt(console, "tfInput");
  }

  public static void focusConsole(String s) {
    setConsoleInput(s);
    focusConsole();
  }

  public static void focusConsole() {
    JComponent tf = consoleInputFieldOrComboBox();
    if (tf != null) {
      tf.requestFocus();
    }
  }

  public static IResourceLoader vm_getResourceLoader() {
    return proxy(IResourceLoader.class, vm_generalMap_get("_officialResourceLoader"));
  }

  public static boolean isLocalSnippetID(String snippetID) {
    return isSnippetID(snippetID) && isLocalSnippetID(psI(snippetID));
  }

  public static boolean isLocalSnippetID(long snippetID) {
    return snippetID >= 1000 && snippetID <= 9999;
  }

  public static 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");
  }

  public static boolean isImageServerSnippet(long id) {
    return id >= 1100000 && id < 1200000;
  }

  public static String imageServerURL() {
    return or2(trim(loadTextFile(javaxDataDir("image-server-url.txt"))), "http://botcompany.de/images/raw/");
  }

  public static String muricaCredentialsQuery() {
    return htmlQuery(muricaCredentials());
  }

  public static boolean isGeneralFileServerSnippet(long id) {
    return id >= 1400000 && id < 1500000;
  }

  public static String tb_mainServer_default = "http://tinybrain.de:8080";

  public static Object tb_mainServer_override;

  public static String tb_mainServer() {
    if (tb_mainServer_override != null)
      return (String) callF(tb_mainServer_override);
    return trim(loadTextFile(tb_mainServer_file(), tb_mainServer_default));
  }

  public static File tb_mainServer_file() {
    return getProgramFile("#1001638", "mainserver.txt");
  }

  public static boolean tb_mainServer_isDefault() {
    return eq(tb_mainServer(), tb_mainServer_default);
  }

  public static String standardCredentials_noCookies() {
    return standardCredentials() + "&noCookies=1";
  }

  public static boolean saveTextFileIfChanged(File f, String contents) {
    return saveTextFileIfDifferent(f, contents);
  }

  public static File snippetTitle_cacheFile(String snippetID) {
    return javaxCachesDir("Snippet Titles/" + psI(snippetID));
  }

  public static String fsI(String id) {
    return formatSnippetID(id);
  }

  public static String fsI(long id) {
    return formatSnippetID(id);
  }

  public static Set<String> tok_returnTypesOfStaticFunction_uncleaned(List<String> tok, String functionName) {
    List<List<String>> funcs = findFullFunctionDefs(tok, true);
    TreeSet<String> out = new TreeSet();
    for (List<String> tokF : funcs) {
      int i = indexOfAny(tokF, 0, "(", "{");
      if (i < 0)
        continue;
      String fname = get(tokF, i - 2);
      if (!eq(fname, functionName))
        continue;
      out.add(joinSubList(tokF, 1, i - 3));
    }
    return out;
  }

  public static <A> boolean containsOneOf(Collection<A> l, A... x) {
    for (A a : unnull(l)) if (eqOneOf(a, x))
      return true;
    return false;
  }

  public static <A> boolean containsOneOf(Collection<A> l, Set<A> set) {
    if (set == null)
      return false;
    for (A a : unnull(l)) if (set.contains(a))
      return true;
    return false;
  }

  public static boolean containsOneOf(String s, String... x) {
    for (String o : x) if (contains(s, o))
      return true;
    return false;
  }

  public static String textOfStandardFunction(String sfName) {
    return loadSnippet_cached(stdFunctions_cached().get(sfName));
  }

  public static void clearTokens(List<String> tok) {
    clearAllTokens(tok);
  }

  public static void clearTokens(List<String> tok, int i, int j) {
    clearAllTokens(tok, i, j);
  }

  public static JavaXClassLoader hotwire_makeClassLoader(List<File> files) {
    Collection<String> toShare = hotwire_classesToShare();
    return nempty(toShare) ? new JavaXClassLoaderWithParent2(null, files, myClassLoader(), cloneList(toShare)) : new JavaXClassLoader(null, files);
  }

  public static String getCanonicalPath(String path) {
    try {
      return new File(path).getCanonicalPath();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static AutoCloseable tempCleaningUp() {
    return tempSetTL(ping_isCleanUpThread, true);
  }

  public static void closeAllWriters(Collection<? extends Writer> l) {
    for (Writer w : unnull(l)) {
      try {
        w.close();
      } catch (Throwable __e) {
        _handleException(__e);
      }
    }
  }

  public static JTextArea jTextAreaWithUndo() {
    return jTextAreaWithUndo("");
  }

  public static JTextArea jTextAreaWithUndo(final String text) {
    return jenableUndoRedo(swingNu(JTextArea.class, text));
  }

  public static boolean containsNewLines(String s) {
    return containsNewLine(s);
  }

  public static String jlabel_textAsHTML_center(String text) {
    return "<html><div style=\"text-align: center;\">" + replace(htmlencode(text), "\n", "<br>") + "</div></html>";
  }

  public static String getSelectedItem(JList l) {
    return (String) l.getSelectedValue();
  }

  public static String getSelectedItem(JComboBox cb) {
    return strOrNull(cb.getSelectedItem());
  }

  public static boolean regexpFinds(String pat, String s) {
    return regexp(pat, s).find();
  }

  public static boolean regexpFinds(Pattern pat, String s) {
    return regexp(pat, s).find();
  }

  public static boolean fileExists(String path) {
    return path != null && new File(path).exists();
  }

  public static boolean fileExists(File f) {
    return f != null && f.exists();
  }

  public static String fromUTF8(byte[] bytes) {
    return fromUtf8(bytes);
  }

  public static File remoteMechListMirrorsDir() {
    return javaxDataDir("Remote Mech Lists");
  }

  public static Object[] muricaCredentialsPlus(Object... params) {
    return concatArrays(muricaCredentials(), params);
  }

  public static String md5OrNull(String s) {
    return s == null ? null : md5(s);
  }

  public static String postPageSilently(String url, Object... params) {
    return doPostSilently(litmap(params), url);
  }

  public static Map jsonDecodeMap(String s) {
    Object o = jsonDecode(s);
    if (o instanceof List && empty((List) o))
      return new HashMap();
    if (o instanceof Map)
      return (Map) o;
    else
      throw fail("Not a JSON map: " + s);
  }

  public static File saveTextFile(String fileName, String contents) throws IOException {
    CriticalAction action = beginCriticalAction("Saving file " + fileName + " (" + l(contents) + " chars)");
    try {
      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());
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8");
        PrintWriter printWriter = new PrintWriter(outputStreamWriter);
        printWriter.print(contents);
        printWriter.close();
      }
      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;
    } finally {
      action.done();
    }
  }

  public static File saveTextFile(File fileName, String contents) {
    try {
      saveTextFile(fileName.getPath(), contents);
      return fileName;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static <A, B> Map<A, B> getMultipleKeys(Map<A, B> map, A... keys) {
    Map<A, B> map2 = similarEmptyMap(map);
    if (map != null)
      for (A key : keys) map2.put(key, map.get(key));
    return map2;
  }

  public static File remoteMechListMirrorNameFile(String listName) {
    return javaxDataDir("Remote Mech Lists/" + uniqueFileNameUsingMD5_80_v2(upper(listName)) + ".name");
  }

  public static int countLines(String s) {
    return l(toLines(s));
  }

  public static File localMechListFile(String listName) {
    return newFile(localMechListsDir(), uniqueFileNameUsingMD5_80_v2(listName) + ".text");
  }

  public static File javaxCachesDir_dir;

  public static File javaxCachesDir() {
    return javaxCachesDir_dir != null ? javaxCachesDir_dir : new File(userHome(), "JavaX-Caches");
  }

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

  public static void saveTextFileUnlessExists(File f, String contents) {
    if (f != null && !f.exists())
      saveTextFile(f, contents);
  }

  public static String lines(Iterable lines) {
    return fromLines(lines);
  }

  public static String lines(Object[] lines) {
    return fromLines(asList(lines));
  }

  public static List<String> lines(String s) {
    return toLines(s);
  }

  public static long recommendedTranspilationDateForStandardFunction(String sfName) {
    return toLong(mechMap("Recommended SF Transpilation Date").get(sfName));
  }

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

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

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

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

  public static List<Pair> _registerDangerousWeakMap_preList;

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

  public static <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;
  }

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

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

  public static File loadBinarySnippet(String snippetID) {
    try {
      IResourceLoader rl = vm_getResourceLoader();
      if (rl != null)
        return rl.loadLibrary(snippetID);
      long id = parseSnippetID(snippetID);
      if (isImageServerSnippet(id))
        return loadImageAsFile(snippetID);
      File f = DiskSnippetCache_getLibrary(id);
      if (fileSize(f) == 0)
        f = loadDataSnippetToFile(snippetID);
      return f;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static void setOptIfNotNull(Object o, String field, Object value) {
    if (value != null)
      setOpt(o, field, value);
  }

  public static Class run(String progID, String... args) {
    Class main = hotwire(progID);
    callMain(main, args);
    return main;
  }

  public static void restart() {
    Object j = getJavaX();
    call(j, "cleanRestart", get(j, "fullArgs"));
  }

  public static volatile boolean sleep_noSleep;

  public static 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);
    }
  }

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

  public static <A> A callMain(A c, String... args) {
    callOpt(c, "main", new Object[] { args });
    return c;
  }

  public static void callMain() {
    callMain(mc());
  }

  public static void setConsoleInput(String text) {
    consoleSetInput(text);
  }

  public static JComponent consoleInputFieldOrComboBox() {
    Object console = get(getJavaX(), "console");
    JComboBox cb = (JComboBox) (getOpt(console, "cbInput"));
    if (cb != null)
      return cb;
    return (JTextField) getOpt(console, "tfInput");
  }

  public static <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 InvocationHandler() {

      public Object invoke(Object proxy, Method method, Object[] args) {
        return call(target, method.getName(), unnull(args));
      }
    });
  }

  public static <A> A proxy(Object target, Class<A> intrface) {
    return proxy(intrface, target);
  }

  public static long psI(String snippetID) {
    return parseSnippetID(snippetID);
  }

  public static File localSnippetFile(long snippetID) {
    return localSnippetsDir(snippetID + ".text");
  }

  public static File localSnippetFile(String snippetID) {
    return localSnippetFile(parseSnippetID(snippetID));
  }

  public static String getFileInfoField(File f, String field) {
    return getOneLineFileInfoField(f, field);
  }

  public static File dropExtension(File f) {
    return f == null ? null : fileInSameDir(f, dropExtension(f.getName()));
  }

  public static String dropExtension(String s) {
    return takeFirst(s, smartLastIndexOf(s, '.'));
  }

  public static String htmlQuery(Map params) {
    return params.isEmpty() ? "" : "?" + makePostData(params);
  }

  public static String htmlQuery(Object... data) {
    return htmlQuery(litmap(data));
  }

  public static Object[] muricaCredentials() {
    String pass = muricaPassword();
    return nempty(pass) ? new Object[] { "_pass", pass } : new Object[0];
  }

  public static String standardCredentials() {
    String user = standardCredentialsUser();
    String pass = standardCredentialsPass();
    if (nempty(user) && nempty(pass))
      return "&_user=" + urlencode(user) + "&_pass=" + urlencode(pass);
    return "";
  }

  public static boolean saveTextFileIfDifferent(File f, String contents) {
    if (eq(loadTextFile(f), contents))
      return false;
    {
      saveTextFile(f, contents);
      return true;
    }
  }

  public static Set<String> findFullFunctionDefs_keywords = new HashSet(splitAtSpace("static svoid ssvoid ssynchronized sbool sS sO sL"));

  public static List<List<String>> findFullFunctionDefs(List<String> tok, boolean topLevelOnly) {
    int n = l(tok);
    List<List<String>> functions = new ArrayList();
    for (int i = 1; i < n; i += 2) {
      String t = tok.get(i);
      if (topLevelOnly && eq(t, "{"))
        i = findEndOfBlock(tok, i) - 1;
      else if (findFullFunctionDefs_keywords.contains(t)) {
        int j = i + 2;
        while (j < n && !eqOneOf(tok.get(j), ";", "=", "(", "{")) j += 2;
        if ((eqGet(tok, j, "(") || eq(t, "svoid") && eqGet(tok, j, "{")) && isIdentifier(tok.get(j - 2))) {
          int k = smartIndexOf(tok, "{", j);
          if (k < l(tok)) {
            k = findEndOfBlock(tok, k) + 1;
            functions.add(subList(tok, i - 1, k));
            i = k - 2;
          }
        }
      }
    }
    return functions;
  }

  public static List<List<String>> findFullFunctionDefs(String s, boolean topLevelOnly) {
    return findFullFunctionDefs(javaTok(s), topLevelOnly);
  }

  public static <A> int indexOfAny(List<A> l, int i, A... x) {
    while (i < l(l)) if (eqOneOf(l.get(i), x))
      return i;
    else
      ++i;
    return -1;
  }

  public static <A> int indexOfAny(List<A> l, Collection<A> x) {
    return indexOfAny(l, 0, x);
  }

  public static <A> int indexOfAny(List<A> l, int i, Collection<A> x) {
    if (nempty(x))
      while (i < l(l)) if (x.contains(l.get(i)))
        return i;
      else
        ++i;
    return -1;
  }

  public static int indexOfAny(String s, int i, String chars) {
    for (; i < l(s); i++) if (chars.indexOf(s.charAt(i)) >= 0)
      return i;
    return -1;
  }

  public static String loadSnippet_cached(String id) {
    return loadSnippet_simpleCache(id);
  }

  public static Map<String, String> stdFunctions_cached_map;

  public static Lock stdFunctions_cached_lock = lock();

  public static Map<String, String> stdFunctions_cached() {
    Lock __1045 = stdFunctions_cached_lock;
    lock(__1045);
    try {
      if (stdFunctions_cached_map == null)
        stdFunctions_cached_map = stdFunctions_uncached();
      return stdFunctions_cached_map;
    } finally {
      unlock(__1045);
    }
  }

  public static synchronized void stdFunctions_clearCache() {
    stdFunctions_cached_map = null;
  }

  public static Set<String> hotwire_classesToShare = synchroSet();

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

  public static ClassLoader myClassLoader() {
    return _getClass(mc()).getClassLoader();
  }

  public static <A extends JTextComponent> A jenableUndoRedo(final A textcomp) {
    {
      swing(new Runnable() {

        public void run() {
          try {
            final UndoManager undo = new UndoManager();
            vm_generalWeakSet("Undo Managers").add(undo);
            textcomp.getDocument().addUndoableEditListener(new UndoableEditListener() {

              public void undoableEditHappened(UndoableEditEvent evt) {
                undo.addEdit(evt.getEdit());
              }
            });
            textcomp.getActionMap().put("Undo", abstractAction("Undo", new Runnable() {

              public void run() {
                try {
                  if (undo.canUndo())
                    undo.undo();
                } catch (Exception __e) {
                  throw rethrow(__e);
                }
              }

              public String toString() {
                return "if (undo.canUndo()) undo.undo()";
              }
            }));
            textcomp.getActionMap().put("Redo", abstractAction("Redo", new Runnable() {

              public void run() {
                try {
                  if (undo.canRedo())
                    undo.redo();
                } catch (Exception __e) {
                  throw rethrow(__e);
                }
              }

              public String toString() {
                return "if (undo.canRedo()) undo.redo()";
              }
            }));
            textcomp.getInputMap().put(KeyStroke.getKeyStroke("control Z"), "Undo");
            textcomp.getInputMap().put(KeyStroke.getKeyStroke("control Y"), "Redo");
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "final new UndoManager undo;\r\n    vm_generalWeakSet(\"Undo Managers\").add(undo)...";
        }
      });
    }
    return textcomp;
  }

  public static <A> A swingNu(final Class<A> c, final Object... args) {
    return swingConstruct(c, args);
  }

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

  public static String htmlencode(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("&#").append((int) c).append(';');
      else
        out.append(c);
    }
    return out.toString();
  }

  public static String fromUtf8(byte[] bytes) {
    try {
      return bytes == null ? null : new String(bytes, "UTF-8");
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static Object[] concatArrays(Object[]... arrays) {
    int l = 0;
    for (Object[] a : arrays) l += l(a);
    Object[] x = new Object[l];
    int i = 0;
    for (Object[] a : arrays) if (a != null) {
      System.arraycopy(a, 0, x, i, l(a));
      i += l(a);
    }
    return x;
  }

  public static String doPostSilently(Map urlParameters, String url) {
    return doPostSilently(makePostData(urlParameters), url);
  }

  public static String doPostSilently(String urlParameters, String url) {
    doPost_silently.set(true);
    return doPost(urlParameters, url);
  }

  public static boolean jsonDecode_useOrderedMaps = true;

  public static Object jsonDecode(final String text) {
    final List<String> tok = jsonTok(text);
    if (l(tok) == 1)
      return null;
    class Y {

      public int i = 1;

      public Object parse() {
        String t = tok.get(i);
        if (t.startsWith("\"")) {
          String s = unquote(tok.get(i));
          i += 2;
          return s;
        }
        if (t.equals("{"))
          return parseMap();
        if (t.equals("["))
          return this.parseList();
        if (t.equals("null")) {
          i += 2;
          return null;
        }
        if (t.equals("false")) {
          i += 2;
          return false;
        }
        if (t.equals("true")) {
          i += 2;
          return true;
        }
        boolean minus = false;
        if (t.equals("-")) {
          minus = true;
          i += 2;
          t = get(tok, i);
        }
        if (isInteger(t)) {
          i += 2;
          if (eq(get(tok, i), ".")) {
            String x = t + "." + get(tok, i + 2);
            i += 4;
            double d = parseDouble(x);
            if (minus)
              d = -d;
            return d;
          } else {
            long l = parseLong(t);
            if (minus)
              l = -l;
            return l != (int) l ? new Long(l) : new Integer((int) l);
          }
        }
        throw new RuntimeException("Unknown token " + (i + 1) + ": " + t + ": " + text);
      }

      public Object parseList() {
        consume("[");
        List list = new ArrayList();
        while (!tok.get(i).equals("]")) {
          list.add(parse());
          if (tok.get(i).equals(","))
            i += 2;
        }
        consume("]");
        return list;
      }

      public Object parseMap() {
        consume("{");
        Map map = jsonDecode_useOrderedMaps ? new LinkedHashMap() : new TreeMap();
        while (!tok.get(i).equals("}")) {
          String key = unquote(tok.get(i));
          i += 2;
          consume(":");
          Object value = parse();
          map.put(key, value);
          if (tok.get(i).equals(","))
            i += 2;
        }
        consume("}");
        return map;
      }

      public void consume(String s) {
        if (!tok.get(i).equals(s)) {
          String prevToken = i - 2 >= 0 ? tok.get(i - 2) : "";
          String nextTokens = join(tok.subList(i, Math.min(i + 4, tok.size())));
          throw fail(quote(s) + " expected: " + prevToken + " " + nextTokens + " (" + i + "/" + tok.size() + ")");
        }
        i += 2;
      }
    }
    return new Y().parse();
  }

  public static List<CriticalAction> beginCriticalAction_inFlight = synchroList();

  public static class CriticalAction {

    public String description;

    public CriticalAction() {
    }

    public CriticalAction(String description) {
      this.description = description;
    }

    public void done() {
      beginCriticalAction_inFlight.remove(this);
    }
  }

  public static CriticalAction beginCriticalAction(String description) {
    ping();
    CriticalAction c = new CriticalAction(description);
    beginCriticalAction_inFlight.add(c);
    return c;
  }

  public static void cleanMeUp_beginCriticalAction() {
    int n = 0;
    while (nempty(beginCriticalAction_inFlight)) {
      int m = l(beginCriticalAction_inFlight);
      if (m != n) {
        n = m;
        try {
          print("Waiting for " + n2(n, "critical actions") + ": " + join(", ", collect(beginCriticalAction_inFlight, "description")));
        } catch (Throwable __e) {
          _handleException(__e);
        }
      }
      sleepInCleanUp(10);
    }
  }

  public static File localMechListsDir() {
    return javaxDataDir("Mech Lists");
  }

  public static String fromLines(Iterable lines) {
    StringBuilder buf = new StringBuilder();
    if (lines != null)
      for (Object line : lines) buf.append(str(line)).append('\n');
    return buf.toString();
  }

  public static String fromLines(String... lines) {
    return fromLines(asList(lines));
  }

  public static Map<String, String> mechMap(String listName) {
    return mechMapCI(listName);
  }

  public static 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 dir = imageSnippetsCacheDir();
      if (loadBufferedImage_useImageCache) {
        dir.mkdirs();
        File file = new File(dir, snippetID + ".png");
        if (file.exists() && file.length() != 0)
          return file;
      }
      String imageURL = snippetImageURL_noHttps(snippetID);
      System.err.println("Loading image: " + imageURL);
      byte[] data = loadBinaryPage(imageURL);
      File file = new File(dir, snippetID + ".png");
      saveBinaryFile(file, data);
      return file;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static 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 void DiskSnippetCache_putLibrary(long snippetID, byte[] data) throws IOException {
    saveBinaryFile(DiskSnippetCache_file(snippetID), data);
  }

  public static 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("http://data.tinybrain.de/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;
  }

  public static File loadDataSnippetToFile(String snippetID) {
    try {
      snippetID = fsI(snippetID);
      IResourceLoader rl = vm_getResourceLoader();
      if (rl != null)
        return rl.loadLibrary(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("http://data.tinybrain.de/blobs/" + psI(snippetID)));
          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) {
        printStackTrace(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);
    }
  }

  public static Object sleepQuietly_monitor = new Object();

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

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

  public static File localSnippetsDir() {
    return javaxDataDir("Personal Programs");
  }

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

  public static String getOneLineFileInfoField(File f, String field) {
    File infoFile = associatedInfosFile(f);
    List<String> lines = lines(loadTextFile(infoFile));
    return firstStartingWithIC_drop(lines, field + ": ");
  }

  public static File fileInSameDir(File f, String newName) {
    return newFile(parentFile(f), newName);
  }

  public static String makePostData(Map<Object, Object> map) {
    List<String> l = new ArrayList();
    for (Map.Entry<Object, Object> e : map.entrySet()) {
      String key = (String) (e.getKey());
      Object val = e.getValue();
      if (val != null) {
        String value = str(val);
        l.add(urlencode(key) + "=" + urlencode((value)));
      }
    }
    return join("&", l);
  }

  public static String makePostData(Object... params) {
    return makePostData(litorderedmap(params));
  }

  public static volatile boolean muricaPassword_pretendNotAuthed;

  public static String muricaPassword() {
    if (muricaPassword_pretendNotAuthed)
      return null;
    return trim(loadTextFile(muricaPasswordFile()));
  }

  public static String standardCredentialsUser() {
    return trim(loadTextFile(oneOfTheFiles(javaxSecretDir("tinybrain-username"), userDir(".tinybrain/username"))));
  }

  public static String standardCredentialsPass() {
    return trim(loadTextFile(oneOfTheFiles(javaxSecretDir("tinybrain-userpass"), userDir(".tinybrain/userpass"))));
  }

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

  public static int findEndOfBlock(List<String> cnc, int i) {
    int j = i + 2, level = 1, n = cnc.size();
    while (j < n) {
      String t = cnc.get(j);
      if ("{".equals(t))
        ++level;
      else if ("}".equals(t))
        --level;
      if (level == 0)
        return j + 1;
      j += 2;
    }
    return n;
  }

  public static ExpiringMap2<String, String> loadSnippet_simpleCache_map = new ExpiringMap2(10000);

  public static Lock loadSnippet_simpleCache_lock = lock();

  public static int loadSnippet_simpleCache_timeout = 60000;

  public static String loadSnippet_simpleCache(String id) {
    if (id == null)
      return null;
    Lock __264 = loadSnippet_simpleCache_lock;
    lock(__264);
    try {
      id = fsI(id);
      String src = loadSnippet_simpleCache_map.get(id);
      if (src != null)
        return src;
      Integer oldTimeout = setThreadLocal(loadPage_forcedTimeout_byThread, loadSnippet_simpleCache_timeout);
      try {
        src = loadSnippet(id);
        if (src != null)
          loadSnippet_simpleCache_map.put(id, src);
        return src;
      } finally {
        loadPage_forcedTimeout_byThread.set(oldTimeout);
      }
    } finally {
      unlock(__264);
    }
  }

  public static Map<String, String> stdFunctions_uncached() {
    return stdFunctions_uncached(new HashMap());
  }

  public static Map<String, String> stdFunctions_uncached(Map<String, String> map) {
    parseStdFunctionsList(loadSnippetSilently("#1006654"), map);
    parseStdFunctionsList(loadSnippetSilently("#761"), map);
    return map;
  }

  public static Set vm_generalWeakSet(Object name) {
    synchronized (get(javax(), "generalMap")) {
      Set set = (Set) (vm_generalMap_get(name));
      if (set == null)
        vm_generalMap_put(name, set = newWeakHashSet());
      return set;
    }
  }

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

  public static ThreadLocal<Long> doPost_timeout = new ThreadLocal();

  public static String doPost(Map urlParameters, String url) {
    return doPost(makePostData(urlParameters), url);
  }

  public static String doPost(String urlParameters, String url) {
    try {
      URL _url = new URL(url);
      ping();
      return doPost(urlParameters, _url.openConnection(), _url);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static String doPost(String urlParameters, URLConnection conn, URL url) {
    try {
      boolean silently = isTrue(optParam(doPost_silently));
      Long timeout = optParam(doPost_timeout);
      setHeaders(conn);
      int l = lUtf8(urlParameters);
      if (!silently)
        print("Sending POST request: " + hideCredentials(url) + " (" + l + " bytes)");
      if (timeout != null)
        setURLConnectionTimeouts(conn, timeout);
      ((HttpURLConnection) conn).setRequestMethod("POST");
      conn.setDoOutput(true);
      conn.setRequestProperty("Content-Length", str(l));
      OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
      writer.write(urlParameters);
      writer.flush();
      String contents = loadPage_utf8(conn, url, false);
      writer.close();
      return contents;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static List<String> jsonTok(String s) {
    List<String> tok = new ArrayList();
    int l = l(s);
    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);
      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.isLetter(c))
        do ++j; while (j < l && Character.isLetter(s.charAt(j)));
      else if (Character.isDigit(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;
  }

  public static void sleepInCleanUp(long ms) {
    try {
      if (ms < 0)
        return;
      Thread.sleep(ms);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static Map<String, Pair<String, Map<String, String>>> mechMapCI_cache = synchroMap();

  public static Map<String, String> mechMapCI(String listName) {
    Pair<String, Map<String, String>> p = mechMapCI_cache.get(listName);
    String text = mechList_raw(listName);
    if (p != null && eq(p.a, text))
      return p.b;
    String parsing = first(mechList_opt(listName + " | Parsing"));
    Map<String, String> map;
    if (parsing == null)
      map = parseDoubleArrowMapCI(tlft(text));
    else
      map = asCIMap((Map<String, String>) callAndMake(parsing, text));
    p = pair(text, map);
    mechMapCI_cache.put(listName, p);
    return p.b;
  }

  public static boolean loadBufferedImage_useImageCache = true;

  public static BufferedImage loadBufferedImage(String snippetIDOrURLOrFile) {
    try {
      if (snippetIDOrURLOrFile == null)
        return null;
      if (isURL(snippetIDOrURLOrFile))
        return imageIO_readURL(snippetIDOrURLOrFile);
      if (isAbsolutePath(snippetIDOrURLOrFile))
        return loadBufferedImage(new File(snippetIDOrURLOrFile));
      if (!isSnippetID(snippetIDOrURLOrFile))
        throw fail("Not a URL or snippet ID or file: " + snippetIDOrURLOrFile);
      String snippetID = "" + parseSnippetID(snippetIDOrURLOrFile);
      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;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static BufferedImage loadBufferedImage(File file) {
    try {
      return file.isFile() ? ImageIO.read(file) : null;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static Map<String, String> parseDoubleLeftArrowMapCI_tlft(String s) {
    return parseDoubleLeftArrowMapCI(tlft(s));
  }

  public static boolean isURL(String s) {
    return startsWithOneOf(s, "http://", "https://", "file:");
  }

  public static File imageSnippetsCacheDir() {
    return javaxCachesDir("Image-Snippets");
  }

  public static String snippetImageURL_noHttps(String snippetID) {
    return snippetImageURL_noHttps(snippetID, "png");
  }

  public static 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/");
  }

  public static ThreadLocal<Map<String, List<String>>> loadBinaryPage_responseHeaders = new ThreadLocal();

  public static ThreadLocal<Map<String, String>> loadBinaryPage_extraHeaders = new ThreadLocal();

  public static byte[] loadBinaryPage(String url) {
    try {
      print("Loading " + url);
      return loadBinaryPage(loadPage_openConnection(new URL(url)));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static 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);
    }
  }

  public static 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";
      FileOutputStream fileOutputStream = newFileOutputStream(tempFileName);
      fileOutputStream.write(contents);
      fileOutputStream.close();
      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);
    }
  }

  public static byte[] saveBinaryFile(File fileName, byte[] contents) {
    return saveBinaryFile(fileName.getPath(), contents);
  }

  public static File getGlobalCache() {
    File file = new File(javaxCachesDir(), "Binary Snippets");
    file.mkdirs();
    return file;
  }

  public static String dataSnippetLink(String snippetID) {
    long id = parseSnippetID(snippetID);
    if (id >= 1100000 && id < 1200000)
      return imageServerURL() + id;
    if (id >= 1400000 && id < 1500000)
      return "http://butter.botcompany.de:8080/files/" + id + "?_pass=" + muricaPassword();
    if (id >= 1200000 && id < 1300000) {
      String pw = muricaPassword();
      if (empty(pw))
        throw fail("Please set 'murica password by running #1008829");
      return "http://butter.botcompany.de:8080/1008823/raw/" + id + "?_pass=" + pw;
    } else
      return "http://eyeocr.sourceforge.net/filestore/filestore.php?cmd=serve&file=blob_" + id + "&contentType=application/binary";
  }

  public static <B, A extends B> A addAndReturn(Collection<B> c, A a) {
    if (c != null)
      c.add(a);
    return a;
  }

  public static void loadBinaryPageToFile(String url, File file) {
    try {
      print("Loading " + url);
      loadBinaryPageToFile(openConnection(new URL(url)), file);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static void loadBinaryPageToFile(URLConnection con, File file) {
    try {
      setHeaders(con);
      loadBinaryPageToFile_noHeaders(con, file);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static void loadBinaryPageToFile_noHeaders(URLConnection con, File file) {
    try {
      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);
    }
  }

  public static URLConnection openConnection(URL url) {
    try {
      ping();
      callOpt(javax(), "recordOpenURLConnection", str(url));
      return url.openConnection();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static File associatedInfosFile(File f) {
    return replaceExtension(f, ".infos");
  }

  public static 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;
  }

  public static String firstStartingWithIC_drop(String prefix, Collection<String> l) {
    return firstStartingWithIC_drop(l, prefix);
  }

  public static File parentFile(File f) {
    return dirOfFile(f);
  }

  public static LinkedHashMap litorderedmap(Object... x) {
    LinkedHashMap map = new LinkedHashMap();
    litmap_impl(map, x);
    return map;
  }

  public static File muricaPasswordFile() {
    return new File(javaxSecretDir(), "murica/muricaPasswordFile");
  }

  public static File oneOfTheFiles(String... paths) {
    if (paths != null)
      for (String path : paths) if (fileExists(path))
        return newFile(path);
    return null;
  }

  public static File oneOfTheFiles(File... files) {
    if (files != null)
      for (File f : files) if (fileExists(f))
        return f;
    return null;
  }

  public static Map<String, String> parseStdFunctionsList(String snippetSrc) {
    return parseStdFunctionsList(snippetSrc, new LinkedHashMap());
  }

  public static Map<String, String> parseStdFunctionsList(String snippetSrc, Map<String, String> map) {
    List<String> tok = javaTok(snippetSrc);
    int i = findCodeTokens(tok, "standardFunctions", "=", "litlist", "(");
    int opening = i + 6;
    int closing = indexOf(tok, ")", opening) - 1;
    for (i = opening + 2; i < closing; i += 4) {
      String[] f = unquote(tok.get(i)).split("/");
      map.put(f[1], f[0]);
    }
    return map;
  }

  public static String loadSnippetSilently(Snippet s) {
    return loadSnippetQuietly(s);
  }

  public static String loadSnippetSilently(String snippetID) {
    return loadSnippetQuietly(snippetID);
  }

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

  public static 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) {
      }
  }

  public static int lUtf8(String s) {
    return l(utf8(s));
  }

  public static 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;
  }

  public static Map<String, String> parseDoubleArrowMapCI(List<String> l) {
    return parseDoubleArrowMap((Map) ciMap(), l);
  }

  public static BufferedImage imageIO_readURL(String url) {
    try {
      if (startsWith(url, "https:"))
        disableCertificateValidation();
      return ImageIO.read(new URL(url));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static boolean isAbsolutePath(String s) {
    return s != null && new File(s).isAbsolute();
  }

  public static boolean isAbsolutePath(File f) {
    return f != null && f.isAbsolute();
  }

  public static String snippetImageURL_http(String snippetID) {
    return snippetImageURL_http(snippetID, "png");
  }

  public static String snippetImageURL_http(String snippetID, String contentType) {
    return replacePrefix("https://", "http://", snippetImageURL(snippetID, contentType)).replace(":8443", ":8080");
  }

  public static Map<String, String> parseDoubleLeftArrowMapCI(List<String> l) {
    return parseDoubleLeftArrowMap((Map) ciMap(), l);
  }

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

  public static String snippetImageURL(String snippetID) {
    return snippetImageURL(snippetID, "png");
  }

  public static String snippetImageURL(String snippetID, String contentType) {
    if (isURL(snippetID))
      return snippetID;
    long id = parseSnippetID(snippetID);
    String url;
    if (id == 1000010 || id == 1000012)
      url = "http://tinybrain.de:8080/tb/show-blobimage.php?id=" + id;
    else if (isImageServerSnippet(id))
      url = imageServerLink(id);
    else
      url = "https://www.botcompany.de:8443/img/" + id;
    return url;
  }

  public static int loadPage_defaultTimeout = 60000;

  public static ThreadLocal<String> loadPage_charset = new ThreadLocal();

  public static boolean loadPage_allowGzip = true, loadPage_debug;

  public static boolean loadPage_anonymous;

  public static int loadPage_verboseness = 100000;

  public static int loadPage_retries = 1;

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

  public static volatile int loadPage_forcedTimeout;

  public static ThreadLocal<Integer> loadPage_forcedTimeout_byThread = new ThreadLocal();

  public static ThreadLocal<Map<String, List<String>>> loadPage_responseHeaders = new ThreadLocal();

  public static ThreadLocal<Map<String, String>> loadPage_extraHeaders = 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 (url.getProtocol().equals("https"))
        disableCertificateValidation();
      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 (loadPageThroughProxy_enabled) {
          print("Trying proxy because of: " + e);
          try {
            return loadPageThroughProxy(str(url));
          } catch (Throwable e2) {
            print("  " + exceptionToStringShort(e2));
          }
        } else if (loadPage_debug)
          print(exceptionToStringShort(e));
        if (tries < loadPage_retries - 1)
          sleepSeconds(1);
      }
      throw e;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static String loadPage_preprocess(String url) {
    if (url.startsWith("tb/"))
      url = tb_mainServer() + "/" + url;
    if (url.indexOf("://") < 0)
      url = "http://" + url;
    return url;
  }

  public static 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);
    }
  }

  public static String loadPage(URL url) {
    return loadPage(url.toExternalForm());
  }

  public static String loadPage(URLConnection con, URL url) throws IOException {
    return loadPage(con, url, true);
  }

  public static String loadPage(URLConnection con, URL url, boolean addHeaders) throws IOException {
    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) {
      }
    vm_generalSubMap("URLConnection per thread").put(currentThread(), con);
    loadPage_responseHeaders.set(con.getHeaderFields());
    InputStream in = null;
    try {
      in = con.getInputStream();
      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;
      }
      StringBuilder buf = new StringBuilder();
      int n = 0;
      while (true) {
        int ch = r.read();
        if (ch < 0)
          break;
        buf.append((char) ch);
        ++n;
        if ((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();
    }
  }

  public static 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");
  }

  public static 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;
  }

  public static <A> A println(A a) {
    return print(a);
  }

  public static File mkdirsFor(File file) {
    return mkdirsForFile(file);
  }

  public static 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(total, 100000))));
        total += n;
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static void renameFile_assertTrue(File a, File b) {
    try {
      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));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static File replaceExtension(File f, String extOld, String extNew) {
    return newFile(replaceExtension(f2s(f), extOld, extNew));
  }

  public static File replaceExtension(File f, String extNew) {
    return replaceExtension(f, fileExtension(f), extNew);
  }

  public static String replaceExtension(String s, String extOld, String extNew) {
    s = dropSuffixIC(addPrefixOptIfNempty(".", extOld), s);
    return s + addPrefixOptIfNempty(".", extNew);
  }

  public static String replaceExtension(String name, String extNew) {
    return replaceExtension(name, fileExtension(name), extNew);
  }

  public static File dirOfFile(File f) {
    return f == null ? null : f.getParentFile();
  }

  public static <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;
  }

  public static String getComputerID_quick() {
    return computerID();
  }

  public static byte[] utf8(String s) {
    return toUtf8(s);
  }

  public static volatile boolean disableCertificateValidation_attempted;

  public static void disableCertificateValidation() {
    try {
      if (disableCertificateValidation_attempted)
        return;
      disableCertificateValidation_attempted = true;
      try {
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {

          public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
          }

          public void checkClientTrusted(X509Certificate[] certs, String authType) {
          }

          public void checkServerTrusted(X509Certificate[] certs, String authType) {
          }
        } };
        HostnameVerifier hv = new HostnameVerifier() {

          public boolean verify(String hostname, SSLSession session) {
            return true;
          }
        };
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(hv);
      } catch (Throwable __e) {
        _handleException(__e);
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static Map<String, String> parseDoubleLeftArrowMap(Map<String, String> map, List<String> l) {
    for (String s : l) {
      List<String> x = splitAtDoubleLeftArrow(s);
      if (l(x) == 2)
        map.put(second(x), first(x));
    }
    return map;
  }

  public static String imageServerLink(String md5OrID) {
    if (possibleMD5(md5OrID))
      return "https://botcompany.de/images/md5/" + md5OrID;
    return imageServerLink(parseSnippetID(md5OrID));
  }

  public static String imageServerLink(long id) {
    return "https://botcompany.de/images/" + id;
  }

  public static boolean networkAllowanceTest(String url) {
    return isAllowed("networkAllowanceTest", url);
  }

  public static final boolean loadPageThroughProxy_enabled = false;

  public static String loadPageThroughProxy(String url) {
    return null;
  }

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

  public static <A> A printWithTime(A a) {
    print(hmsWithColons() + ": " + a);
    return a;
  }

  public static Map vm_generalSubMap(Object name) {
    synchronized (get(javax(), "generalMap")) {
      Map map = (Map) (vm_generalMap_get(name));
      if (map == null)
        vm_generalMap_put(name, map = synchroMap());
      return map;
    }
  }

  public static String toHex(byte[] bytes) {
    return bytesToHex(bytes);
  }

  public static String toHex(byte[] bytes, int ofs, int len) {
    return bytesToHex(bytes, ofs, len);
  }

  public static Matcher regexpMatcher(String pat, String s) {
    return compileRegexp(pat).matcher(unnull(s));
  }

  public static 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;
  }

  public static int roundDownTo(int x, int n) {
    return x / n * n;
  }

  public static long roundDownTo(long x, long n) {
    return x / n * n;
  }

  public static String fileExtension(File f) {
    if (f == null)
      return null;
    return fileExtension(f.getName());
  }

  public static String fileExtension(String s) {
    return substring(s, smartLastIndexOf(s, '.'));
  }

  public static String dropSuffixIC(String suffix, String s) {
    return s == null ? null : ewic(s, suffix) ? s.substring(0, l(s) - l(suffix)) : s;
  }

  public static String addPrefixOptIfNempty(String prefix, String s) {
    return addPrefixIfNotEmpty2(prefix, s);
  }

  public static String _computerID;

  public static Lock computerID_lock = lock();

  public static String computerID() {
    if (_computerID == null) {
      Lock __1914 = computerID_lock;
      lock(__1914);
      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(__1914);
      }
    }
    return _computerID;
  }

  public static boolean possibleMD5(String s) {
    return isMD5(s);
  }

  public static volatile Object isAllowed_function;

  public static volatile boolean isAllowed_all = true;

  public static 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));
  }

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

  public static String hmsWithColons() {
    return hmsWithColons(now());
  }

  public static String hmsWithColons(long time) {
    return new SimpleDateFormat("HH:mm:ss").format(time);
  }

  public static String addPrefixIfNotEmpty2(String prefix, String s) {
    return empty(s) ? "" : addPrefix(prefix, s);
  }

  public static File computerIDFile() {
    return javaxDataDir("Basic Info/computer-id.txt");
  }

  public static boolean isMD5(String s) {
    return l(s) == 32 && isLowerHexString(s);
  }

  public static String addPrefix(String prefix, String s) {
    return s.startsWith(prefix) ? s : prefix + s;
  }

  public static 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;
  }

  public static final class _MethodCache {

    public final Class c;

    public final HashMap<String, List<Method>> cache = new HashMap();

    public _MethodCache(Class c) {
      this.c = c;
      _init();
    }

    public void _init() {
      Class _c = c;
      while (_c != null) {
        for (Method m : _c.getDeclaredMethods()) multiMapPut(cache, m.getName(), makeAccessible(m));
        _c = _c.getSuperclass();
      }
      for (Class intf : allInterfacesImplementedBy(c)) for (Method m : intf.getDeclaredMethods()) if (m.isDefault())
        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);
      }
    }
  }

  public static class LineBuffer {

    public VF1<String> onLine;

    public StringBuilder currentLine = new StringBuilder();

    public LineBuffer() {
    }

    public LineBuffer(VF1<String> onLine) {
      this.onLine = onLine;
    }

    public void append(String s) {
      append(s, onLine);
    }

    public void append(String s, VF1<String> onLine) {
      currentLine.append(s);
      if (contains(s, '\n')) {
        int i = 0, j;
        s = str(currentLine);
        while ((j = indexOf(s, i, '\n')) >= 0) {
          String line = dropTrailingBackslashR(substring(s, i, j));
          callF(onLine, line);
          i = j + 1;
        }
        currentLine = new StringBuilder(substring(s, i));
      }
    }

    public int size() {
      return l(currentLine);
    }
  }

  public static class GazelleEvalContext {

    public RuleEngine2 engine;

    public Map<String, ai_gazelle_analyzeStatementsForRule_Data> dataByRule;

    public Set<String> linesSet;

    public transient List onNewNode;

    public GazellePredictor predictor;

    public String mechListPrefix = "";

    public GazelleEvalContext() {
      gazelle_addSharedClasses();
    }
  }

  public abstract static class VF2<A, B> {

    public abstract void get(A a, B b);
  }

  public static class GazelleTree {

    public List<GazelleTree> children;

    public GazelleTree parent;

    public double weight = 1, totalWeight;

    public String line, lineType;

    public String prediction, judgement;

    public RuleEngine2_MatchedRule mr;

    public boolean isSplitNode;

    public long created = now();

    public transient GazelleEvalContext ctx;

    public GazelleTree() {
    }

    public GazelleTree(String line) {
      this.line = line;
    }

    public GazelleTree(GazelleEvalContext ctx, String line) {
      this.line = line;
      this.ctx = ctx;
    }

    public String toString() {
      return (isSplitNode ? "[split] " : "") + line + appendSquareBracketed(joinWithComma(listPlusNempties(renderQualityElements(), ruleID())));
    }

    public List<String> renderQualityElements() {
      List<String> l = new ArrayList();
      addIfNempty(l, prediction);
      if (mr != null) {
        l.add(mr.qualitySum() + " / " + formatDouble(mr.relativeQuality(), 2));
        if (mr.moreConditions())
          l.add("unsatisfied");
      }
      return l;
    }

    public List<String> history() {
      List<String> l = new ArrayList();
      GazelleTree e = this;
      while (e != null) {
        l.add(e.line);
        e = e.parent;
      }
      return reversed(l);
    }

    public void add(GazelleTree child) {
      if (children == null)
        children = new ArrayList();
      child.parent = this;
      child.setContext(ctx);
      children.add(child);
    }

    public void setContext(GazelleEvalContext ctx) {
      this.ctx = ctx;
      pcallFAll(ctx.onNewNode, this);
      _add(ctx.linesSet, line);
    }

    public RuleEngine2.Rule rule() {
      return mr == null ? null : mr.rule;
    }

    public String ruleID() {
      return rule() == null ? null : rule().globalID;
    }

    public Map<String, String> varMap() {
      return mr == null ? null : mr.map;
    }

    public void flush() {
      if (mr != null) {
        mr.flush();
        line = mr.outText();
      }
    }

    public Collection<String> ruleComments() {
      RuleEngine2.Rule rule = rule();
      return rule == null ? null : rule.comments;
    }
  }

  public static class Var<A> implements IVar<A> {

    public A v;

    public Var() {
    }

    public Var(A v) {
      this.v = 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 synchronized void clear() {
      v = null;
    }

    public String toString() {
      return str(get());
    }
  }

  public abstract static interface GazellePredictor {

    public String get(GazelleEvalContext ctx, RuleEngine2_MatchedRule mr);
  }

  public static class MechListDependentCache<A> extends Cache<A> {

    public String listName, lastListContents;

    public MechListDependentCache() {
    }

    public MechListDependentCache(String listName, Object maker) {
      this.maker = maker;
      this.listName = listName;
    }

    public A make() {
      return (A) callF(maker, lastListContents);
    }

    public A get() {
      if (hasLock(lock))
        return value;
      {
        String listContents = mL_raw(listName);
        Lock __2463 = lock;
        lock(__2463);
        try {
          if (!eqOrSame(listContents, lastListContents))
            clear();
          lastListContents = listContents;
        } finally {
          unlock(__2463);
        }
      }
      return super.get();
    }
  }

  public abstract static class TokCondition {

    public abstract boolean get(List<String> tok, int i);
  }

  public static class Gazelle_ReasoningForLine {

    public long timestamp = now();

    public long outMsgID;

    public String inContext, outContext;

    public String outText;

    public long inMsgID, inUserID, inChannelID;

    public String inUser;

    public String inText;

    public GazelleTree tree;

    public int treeIndex;
  }

  public static class MechListDependentCaches<A> {

    public Map<String, MechListDependentCache<A>> byList = ciMap();

    public Object maker;

    public transient Lock lock = lock();

    public MechListDependentCaches(Object maker) {
      this.maker = maker;
    }

    public A get(String name) {
      MechListDependentCache<A> listCache;
      {
        Lock __3573 = lock;
        lock(__3573);
        try {
          listCache = byList.get(name);
          if (listCache == null)
            byList.put(name, listCache = new MechListDependentCache(name, maker));
        } finally {
          unlock(__3573);
        }
      }
      return listCache.get();
    }
  }

  public static class MultiMap<A, B> {

    public Map<A, List<B>> data = new HashMap<A, List<B>>();

    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);
      }
    }

    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);
      }
    }

    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 List<B> get(A key) {
      synchronized (data) {
        List<B> list = data.get(key);
        return list == null ? Collections.<B>emptyList() : list;
      }
    }

    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())
          data.remove(key);
      }
    }

    public Set<A> keySet() {
      synchronized (data) {
        return data.keySet();
      }
    }

    public Set<A> keys() {
      synchronized (data) {
        return data.keySet();
      }
    }

    public void remove(A key) {
      synchronized (data) {
        data.remove(key);
      }
    }

    public void remove(A key, B value) {
      synchronized (data) {
        List<B> list = data.get(key);
        if (list != null) {
          list.remove(value);
          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 int keysSize() {
      synchronized (data) {
        return l(data);
      }
    }

    public int size() {
      synchronized (data) {
        int n = 0;
        for (List l : data.values()) n += l(l);
        return n;
      }
    }

    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 List<B> allValues() {
      return concatLists(values(data));
    }

    public Object mutex() {
      return data;
    }

    public String toString() {
      return "mm" + str(data);
    }
  }

  public static class QuickException extends RuntimeException {

    public Throwable fillInStackTrace() {
      return this;
    }

    public QuickException() {
    }

    public QuickException(Throwable e) {
      super(e);
    }

    public QuickException(String msg) {
      super(msg);
    }

    public QuickException(String msg, Throwable e) {
      super(msg, e);
    }
  }

  public static class RuleEngine2 {

    public static class SimplifyWithRule extends Rule {

      public transient Object _function;

      public boolean isSplitRule;

      public boolean callOnTree;

      public Object function() {
        if (_function == null) {
          gazelle_parseInstruction(this);
          assertNotNull(_function);
        }
        return _function;
      }
    }

    public abstract static class QuickCheck {

      public abstract boolean check(String input);
    }

    public static class CicQuickCheck extends QuickCheck {

      public String string;

      public boolean check(String input) {
        return cic(input, string);
      }
    }

    public static class Rule {

      public String globalID;

      public List<String> in;

      public String out;

      public List<String> comments;

      public Set<String> vars;

      public List<GRuleLine> insWithType;

      public List<String> outTypes;

      public transient List inputMassagers;

      public transient List checkerFunctions;

      public transient List matchers;

      public String text;

      public QuickCheck applicabilityQuickCheck;

      public String purpose = "";

      public Double qualityCutOff;

      public boolean avoidRecentlyUsedMappings;

      public List mapMassagers;

      public String asText() {
        if (l(in) > 1 && eq(last(in), out))
          return join(" \n+ ", dropLast(in)) + " \n+=> " + out;
        return join(" \n+ ", in) + " \n=> " + out;
      }

      public void addMapMassager(Object massager) {
        if (massager != null)
          mapMassagers = addToOrCreateList(mapMassagers, massager);
      }

      public void setInputMassager(int i, Object massager) {
        inputMassagers = listSetWithCreate(inputMassagers, i, massager);
      }

      public void setMatcher(int i, Object matcher) {
        matchers = listSetWithCreate(matchers, i, matcher);
      }

      public void setOutType(int i, String type) {
        outTypes = listSetWithCreate(outTypes, i, type);
      }

      public String outType() {
        return first(outTypes);
      }

      public void parseGeneralComments() {
        purpose = gazelle_purposeFromComments(comments);
        try {
          String s = matchAny_firstGroup("quality cutoff = *", comments);
          if (s != null)
            qualityCutOff = parseDouble(s);
        } catch (Throwable __e) {
          _handleException(__e);
        }
        avoidRecentlyUsedMappings = cic(comments, "avoid recently used mapping");
      }

      public String toString() {
        return globalID + ": " + text;
      }
    }

    public int minScore = 50;

    public int maxLevel = 100, maxOutLines = 10000;

    public List<Rule> rules = new ArrayList();

    public boolean printMappings, printGroupedData, printRejectedVars;

    public Set<String> facts = ciSet();

    public transient F2<String, String, Object> callFunctionOnString = new F2<String, String, Object>() {

      public Object get(String function, String s) {
        try {
          return callAndMake(function, s);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "callAndMake(function, s)";
      }
    };

    public boolean hasHelpers;

    public RuleEngine2() {
    }

    public RuleEngine2(List<Pair<String, String>> rulesWithComments) {
      for (Pair<String, String> p : unnull(rulesWithComments)) addRule(p.a, splitAtDoubleArrow_pair(p.a), p.b, null);
    }

    public void copyRulesFrom(RuleEngine2 engine) {
      if (engine == null)
        return;
      rules.addAll(engine.rules);
      hasHelpers = engine.hasHelpers;
    }

    public void addRules2(List<T3<String, String, String>> rulesWithCommentsAndID) {
      Matches m = new Matches();
      for (T3<String, String, String> p : unnull(rulesWithCommentsAndID)) try {
        Pair<String, String> p2 = splitAtDoubleArrow_pair(p.a);
        if (p2 != null) {
          addRule(p.a, p2, p.b, p.c);
          continue;
        }
        SimplifyWithRule r = new SimplifyWithRule();
        r.globalID = p.c;
        r.text = p.a;
        r.comments = lines(p.b);
        r.parseGeneralComments();
        String s = p.a;
        gazelle_parseInstruction(r);
        if (r._function == null)
          continue;
        rules.add(r);
      } catch (Throwable e) {
        printStackTrace("Exception parsing rule " + p.c, e);
      }
    }

    public void addRule(String text, Pair<String, String> rule, String comments, String globalID) {
      if (rule == null)
        return;
      rule = ai_rule_pair_expandPlusArrow(rule);
      List<String> conditions = tok_splitAtPlusAtBeginningOfLine(rule.a);
      Rule r = new Rule();
      r.globalID = globalID;
      r.text = text;
      r.comments = lines(comments);
      r.parseGeneralComments();
      r.in = conditions;
      r.insWithType = map(conditions, new F1<String, GRuleLine>() {

        public GRuleLine get(String s) {
          try {
            return new GRuleLine(s, "standard");
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "GRuleLine(s, \"standard\")";
        }
      });
      for (int i = 0; i < l(r.in); i++) {
        String s = r.in.get(i);
        {
          if (!(endsWith(s, "<anything>")))
            continue;
        }
        if (eq(s, "<anything>"))
          r.checkerFunctions = listSetWithCreate(r.checkerFunctions, i, new F1<String, Map<String, String>>() {

            public Map<String, String> get(String s) {
              try {
                return nempty(s) ? litmap() : null;
              } catch (Exception __e) {
                throw rethrow(__e);
              }
            }

            public String toString() {
              return "nempty(s) ? litmap() : null";
            }
          });
        else if (jmatch("* says: <anything>", javaTokWithBrackets(s))) {
          r.setInputMassager(i, new F1<String, String>() {

            public String get(String s) {
              try {
                Matches m = new Matches();
                return jMatchStart("* says:", javaTokWithBrackets(s), m) ? m.get(0) + " says: <anything>" : s;
              } catch (Exception __e) {
                throw rethrow(__e);
              }
            }

            public String toString() {
              return "new Matches m; ret jMatchStart(\"* says:\", javaTokWithBrackets(s), m) ? m.get(...";
            }
          });
        }
      }
      for (Matches m : getJMatches_all("expand *", r.comments)) if (isQuoted(m.get(0))) {
        final String var = m.unq(0);
        for (int i = 0; i < l(r.in); i++) r.setMatcher(i, new Object() {

          public Map<String, String> get(List<String> tokC, List<String> tokI, RuleEngine2_MatchedRule matched) {
            if (eqic(nextToLast(tokC), var))
              tokI = tok_groupLastTokensToMatchPattern(tokI, tokC);
            return zipCodeTokensToCIMap_strict_withoutEquals(tokC, tokI);
          }
        });
      }
      if (jmatchAny(ll("in = dialog", "fact + event => replacement fact"), r.comments))
        for (int i = 0; i < l(r.insWithType); i++) r.insWithType.get(i).type = "dialog-" + (l(r.insWithType) - i);
      Matches m = new Matches();
      for (String s : r.comments) {
        if (jMatchStart("in * =", s, m) && isInteger(m.unq(0)) && (startsWith(m.unq(1), "statement") || eq(m.unq(1), "condition")))
          set(get(r.insWithType, parseInt(m.unq(0)) - 1), "type", m.unq(1));
        else if (jMatchStart("out * =", s, m) && isInteger(m.unq(0)))
          r.setOutType(parseInt(m.unq(0)) - 1, m.unq(1));
        else if (jMatchStart("out = ", s, m))
          r.setOutType(0, m.rest());
        else if (jMatchStart("in * :", s, m) && isInteger(m.unq(0))) {
          int i = parseInt(m.unq(0)) - 1;
          String comment = m.rest();
          GRuleLine line = get(r.insWithType, i);
          if (line != null) {
            line.comments = addToOrCreateList(line.comments, comment);
            if (match("tokenize with *", comment, m))
              line.tokenizer = m.unq(0);
          }
        }
      }
      r.out = rule.b;
      r.vars = ai_wordsInBothSidesOfPair_uncurly(rule);
      rules.add(r);
    }

    public List<Pair<List<String>, String>> rulesAsPairs() {
      return map(rules, new F1<Rule, Pair<List<String>, String>>() {

        public Pair<List<String>, String> get(Rule r) {
          try {
            return pair(r.in, r.out);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "pair(r.in, r.out)";
        }
      });
    }

    public List<SimplifyWithRule> splitterRules() {
      return (List) filter(rules, new F1<Rule, Boolean>() {

        public Boolean get(Rule r) {
          try {
            return r instanceof SimplifyWithRule && ((SimplifyWithRule) r).isSplitRule;
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "r instanceof SimplifyWithRule && ((SimplifyWithRule) r).isSplitRule";
        }
      });
    }

    public void dropRulesWhere(IF1<Rule, Boolean> pred) {
      rules = antiFilter(pred, rules);
    }

    public void deleteRule(String ruleID) {
      dropRulesWhere(r -> eq(r.globalID, ruleID));
    }

    public Rule getRule(String ruleID) {
      return firstWhere(rules, "globalID", ruleID);
    }
  }

  public static class Lowest<A> {

    public A best;

    public double score;

    public transient Object onChange;

    public synchronized boolean isNewBest(double score) {
      return best == null || score < this.score;
    }

    public synchronized double bestScore() {
      return best == null ? Double.NaN : score;
    }

    public double score() {
      return bestScore();
    }

    public synchronized float floatScore() {
      return best == null ? Float.NaN : (float) score;
    }

    public synchronized 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;
    }

    public synchronized A get() {
      return best;
    }

    public synchronized boolean has() {
      return best != null;
    }
  }

  public static class TextAndType {

    public String text;

    public String type;

    public TextAndType() {
    }

    public TextAndType(String text, String type) {
      this.type = type;
      this.text = text;
    }

    public String toString() {
      return "TextAndType(" + text + ", " + type + ")";
    }

    public boolean equals(Object o) {
      if (!(o instanceof TextAndType))
        return false;
      TextAndType x = (TextAndType) o;
      return eq(text, x.text) && eq(type, x.type);
    }

    public int hashCode() {
      int h = -34343004;
      h = h * 2 + _hashCode(text);
      h = h * 2 + _hashCode(type);
      return h;
    }
  }

  public static 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);

    public File pathToJavaXJar();

    public File getSnippetJar(String snippetID, String transpiledSrc);
  }

  public abstract static class Collector<A> {

    public abstract boolean add(A a);

    public boolean full() {
      return false;
    }

    public boolean contains(A a) {
      return false;
    }
  }

  public static class Either<A, B> {

    public byte which;

    public Object value;

    public Either() {
    }

    public Either(int which, Object value) {
      this.value = value;
      this.which = (byte) which;
    }

    public boolean isA() {
      return which == 1;
    }

    public boolean isB() {
      return which == 2;
    }

    public A a() {
      if (which != 1)
        _failMe();
      return (A) value;
    }

    public B b() {
      if (which != 2)
        _failMe();
      return (B) value;
    }

    public void _failMe() {
      throw fail("Either object is of wrong type: " + shortClassName(value));
    }
  }

  public static class MultiSet<A> {

    public Map<A, Integer> map = new HashMap();

    public MultiSet(boolean useTreeMap) {
      if (useTreeMap)
        map = new TreeMap();
    }

    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 void add(A key) {
      add(key, 1);
    }

    public synchronized void addAll(Iterable<A> c) {
      if (c != null)
        for (A a : c) add(a);
    }

    public synchronized void addAll(MultiSet<A> ms) {
      for (A a : ms.keySet()) add(a, ms.get(a));
    }

    public synchronized void add(A key, int count) {
      if (count <= 0)
        return;
      if (map.containsKey(key))
        map.put(key, map.get(key) + count);
      else
        map.put(key, count);
    }

    public synchronized int get(A key) {
      Integer i = map.get(key);
      return i != null ? i : 0;
    }

    public synchronized boolean contains(A key) {
      return map.containsKey(key);
    }

    public synchronized void remove(A key) {
      Integer i = map.get(key);
      if (i != null && i > 1)
        map.put(key, i - 1);
      else
        map.remove(key);
    }

    public synchronized List<A> topTen() {
      return getTopTen();
    }

    public synchronized List<A> getTopTen() {
      return getTopTen(10);
    }

    public synchronized List<A> getTopTen(int maxSize) {
      List<A> list = getSortedListDescending();
      return list.size() > maxSize ? list.subList(0, maxSize) : list;
    }

    public synchronized List<A> highestFirst() {
      return getSortedListDescending();
    }

    public synchronized List<A> lowestFirst() {
      return reversedList(getSortedListDescending());
    }

    public synchronized 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;
    }

    public synchronized int getNumberOfUniqueElements() {
      return map.size();
    }

    public synchronized int uniqueSize() {
      return map.size();
    }

    public synchronized Set<A> asSet() {
      return map.keySet();
    }

    public synchronized NavigableSet<A> navigableSet() {
      return navigableKeys((NavigableMap) map);
    }

    public synchronized Set<A> keySet() {
      return map.keySet();
    }

    public synchronized 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;
    }

    public synchronized void removeAll(A key) {
      map.remove(key);
    }

    public synchronized int size() {
      int size = 0;
      for (int i : map.values()) size += i;
      return size;
    }

    public synchronized 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();
    }

    public synchronized String toString() {
      return str(map);
    }

    public synchronized void clear() {
      map.clear();
    }

    public synchronized Map<A, Integer> asMap() {
      return cloneMap(map);
    }
  }

  public static class GInterpretable {

    public String text;

    public String ruleID;

    public Object mr;

    public GInterpretable() {
    }

    public GInterpretable(String text) {
      this.text = text;
    }

    public GInterpretable(String text, String ruleID) {
      this.ruleID = ruleID;
      this.text = text;
    }

    public GInterpretable(String text, String ruleID, Object mr) {
      this.mr = mr;
      this.ruleID = ruleID;
      this.text = text;
    }

    public String toString() {
      return text;
    }
  }

  public static class GazelleContextCache_v2 extends F0<GazelleEvalContext> {

    public transient Lock lock = lock();

    public transient GazelleEvalContext cachedCtx;

    public DynModule module;

    public boolean debug = true;

    public F0<List<T3<String, String, String>>> grabRules = new F0<List<T3<String, String, String>>>() {

      public List<T3<String, String, String>> get() {
        try {
          return gazelle_allRuleTriples();
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "//dm_gazelle_allRulesWithComment(gazelle_requiredComment())\r\n    ret gazelle_...";
      }
    };

    public String mechListPrefix;

    public boolean alwaysFullRefresh;

    public GazelleContextCache_v2() {
    }

    public GazelleContextCache_v2(DynModule module) {
      this.module = module;
    }

    public GazelleContextCache_v2 fill() {
      loadRules(callF(grabRules));
      return this;
    }

    public void loadRules(List<T3<String, String, String>> rules) {
      Lock __2492 = lock;
      lock(__2492);
      try {
        cachedCtx = dm_gazelle_stdEvalContext(rules);
        cachedCtx.mechListPrefix = unnull(mechListPrefix);
        gazelle_addHelpers(cachedCtx);
      } finally {
        unlock(__2492);
      }
    }

    public GazelleEvalContext get() {
      Lock __2493 = lock;
      lock(__2493);
      try {
        return gazelle_cloneCtx(cachedCtx);
      } finally {
        unlock(__2493);
      }
    }

    public void ruleChanged(String ruleID) {
      if (ruleID == null)
        return;
      dm_q(module, new Runnable() {

        public void run() {
          try {
            Lock __2494 = lock;
            lock(__2494);
            try {
              if (alwaysFullRefresh) {
                fill();
                return;
              }
              if (cachedCtx == null)
                return;
              print("GazelleContextCache_v2: processing rule update " + ruleID);
              RuleEngine2 engine = cachedCtx.engine;
              RuleEngine2.Rule rule = engine.getRule(ruleID);
              if (rule == null)
                print("GazelleContextCache_v2: rule not found, assuming it's new");
              else
                engine.rules.remove(rule);
              Pair<String, String> p = dm_textAndCommentForRule(ruleID);
              if (p == null) {
                print("GazelleContextCache_v2: no text found for rule");
                return;
              }
              engine.addRules2(ll(t3(p.a, p.b, ruleID)));
              rule = cachedCtx.engine.getRule(ruleID);
              if (rule == null) {
                print("GazelleContextCache_v2: rule not properly added");
                return;
              }
              gazelle_addHelpers(nu(GazelleEvalContext.class, "engine", gazelle_dummyEngineWithRule(rule)));
              gazelle_updateStatsForRule(cachedCtx, ruleID);
              print("GazelleContextCache_v2: rule update complete");
            } finally {
              unlock(__2494);
            }
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "Lock __2494 = lock; lock(__2494); try {\r\n      if (alwaysFullRefresh) return ...";
        }
      });
    }

    public AutoCloseable listenToMessages() {
      return combineAutoCloseables(vmBus_onMessage("gazellePleaseClearRuleCaches", new Runnable() {

        public void run() {
          try {
            dm_q(module, new Runnable() {

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

              public String toString() {
                return "fill();";
              }
            });
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "dm_q(module, r fill)";
        }
      }), vmBus_onMessage("gazelleRuleFeedbackChanged", new VF1<String>() {

        public void get(String a) {
          try {
            ruleChanged(a);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "ruleChanged(a)";
        }
      }), vmBus_onMessage("gazelleRuleChanged", new VF1<Object>() {

        public void get(Object ruleObj) {
          try {
            ruleChanged(getString("globalID", ruleObj));
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "ruleChanged(getString globalID(ruleObj))";
        }
      }), vmBus_onMessage("gazelleRuleDeleted", new VF1<Object>() {

        public void get(Object rule) {
          try {
            String ruleID = getString("globalID", rule);
            dm_q(module, new Runnable() {

              public void run() {
                try {
                  Lock __2495 = lock;
                  lock(__2495);
                  try {
                    if (cachedCtx == null)
                      return;
                    cachedCtx.engine.deleteRule(ruleID);
                    print("GazelleContextCache_v2: rule " + ruleID + " deleted");
                  } finally {
                    unlock(__2495);
                  }
                } catch (Exception __e) {
                  throw rethrow(__e);
                }
              }

              public String toString() {
                return "Lock __2495 = lock; lock(__2495); try {\r\n          if (cachedCtx == null) ret...";
              }
            });
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "S ruleID = getString globalID(rule);\r\n        dm_q(module, r {\r\n          loc...";
        }
      }));
    }

    public void alwaysFullRefresh() {
      alwaysFullRefresh = true;
      fill();
    }
  }

  public static class CompilerBot {

    public static boolean verbose;

    public static File compileSnippet(String snippetID) {
      return compileSnippet(snippetID, "");
    }

    public static Pair<File, String> compileSnippet2(String snippetID) {
      return compileSnippet2(snippetID, "");
    }

    public static File compileSnippet(String snippetID, String javaTarget) {
      return compileSnippet2(snippetID, javaTarget).a;
    }

    public static Pair<File, String> compileSnippet2(String snippetID, String javaTarget) {
      String transpiledSrc = getServerTranspiled2(snippetID);
      int i = transpiledSrc.indexOf('\n');
      String libs = transpiledSrc.substring(0, Math.max(0, i));
      if (verbose)
        print("Compiling snippet: " + snippetID + ". Libs: " + libs);
      transpiledSrc = transpiledSrc.substring(i + 1);
      return pair(compile(transpiledSrc, libs, javaTarget, snippetID), transpiledSrc);
    }

    public static File compile(String src) {
      return compile(src, "");
    }

    public static File compile(String src, String libs) {
      return compile(src, libs, null);
    }

    public static File compile(String src, String dehlibs, String javaTarget) {
      return compile(src, dehlibs, javaTarget, null);
    }

    public static File compile(String src, String dehlibs, String javaTarget, String progID) {
      if (verbose)
        print("Compiling " + l(src) + " chars");
      String md5 = md5(dehlibs + "\n" + src + "\n" + progID);
      File jar = getJarFile(md5);
      if (jar == null || jar.length() <= 22) {
        List<String> mainClass = findMainClass(javaTok(src));
        boolean canRename = mainClass != null && useDummyMainClasses() && isSnippetID(progID) && !tok_classHasModifier(mainClass, "public");
        if (verbose)
          print("useRenaming: " + useDummyMainClasses() + ", canRename: " + canRename + ", progID: " + progID);
        javaCompileToJar_optionalRename(src, dehlibs, jar, canRename ? progID : null, progID);
      } else {
        if (verbose)
          print("Getting classes from cache (" + jar.getAbsolutePath() + ", " + jar.length() + " bytes)");
        touchFile(jar);
      }
      return jar;
    }

    public static File getJarFile(String md5) {
      assertTrue(isMD5(md5));
      return new File(getCacheProgramDir("#1002203"), md5 + ".jar");
    }
  }

  public static 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 class Pair<A, B> implements Comparable<Pair<A, B>> {

    public A a;

    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);
    }
  }

  public static class GazelleReaction {

    public static String _fieldOrder = "created userID emoji";

    public long created;

    public long userID;

    public String emoji;

    public GazelleReaction() {
    }

    public GazelleReaction(long created, long userID, String emoji) {
      this.emoji = emoji;
      this.userID = userID;
      this.created = created;
    }

    public String toString() {
      return "GazelleReaction(" + created + ", " + userID + ", " + emoji + ")";
    }

    public boolean equals(Object o) {
      if (!(o instanceof GazelleReaction))
        return false;
      GazelleReaction x = (GazelleReaction) o;
      return eq(created, x.created) && eq(userID, x.userID) && eq(emoji, x.emoji);
    }

    public int hashCode() {
      int h = 1661428073;
      h = h * 2 + _hashCode(created);
      h = h * 2 + _hashCode(userID);
      h = h * 2 + _hashCode(emoji);
      return h;
    }
  }

  public static class GazelleLine {

    public static String _fieldOrder = "timestamp text msgID reactions user bot extras context";

    public long timestamp;

    public String text;

    public long msgID;

    public List<GazelleReaction> reactions;

    public GazelleLine() {
    }

    public GazelleLine(long timestamp, String text, long msgID, List<GazelleReaction> reactions) {
      this.reactions = reactions;
      this.msgID = msgID;
      this.text = text;
      this.timestamp = timestamp;
    }

    public String toString() {
      return "GazelleLine(" + timestamp + ", " + text + ", " + msgID + ", " + reactions + ")";
    }

    public boolean equals(Object o) {
      if (!(o instanceof GazelleLine))
        return false;
      GazelleLine x = (GazelleLine) o;
      return eq(timestamp, x.timestamp) && eq(text, x.text) && eq(msgID, x.msgID) && eq(reactions, x.reactions);
    }

    public int hashCode() {
      int h = -1898653900;
      h = h * 2 + _hashCode(timestamp);
      h = h * 2 + _hashCode(text);
      h = h * 2 + _hashCode(msgID);
      h = h * 2 + _hashCode(reactions);
      return h;
    }

    public String user;

    public boolean bot;

    public Map extras;

    public String context;
  }

  public static class RuleEngine2_MatchedRule {

    public String globalID = aGlobalID();

    public RuleEngine2.Rule rule;

    public int iCond;

    public Map<String, String> map;

    public Map<String, Integer> mappingQuality = new HashMap();

    public List<Pair<List<String>, List<String>>> matchedLines = new ArrayList();

    public String tokenizationFunction;

    public String outTokenizer;

    public List<String> remainingConditions;

    public List<TextAndType> remainingConditionsWithType;

    public Integer qualitySum;

    public Integer nPossibleVars;

    public String out;

    public Map<String, String> inputsByType;

    public GInterpretable interpretable;

    public int qualitySum() {
      if (qualitySum == null)
        qualitySum = intSum(values(mappingQuality)) - l(map);
      return qualitySum;
    }

    public int nPossibleVars() {
      if (nPossibleVars == null)
        nPossibleVars = l(possibleVars());
      return nPossibleVars;
    }

    public boolean isSpecialRule() {
      return rule instanceof RuleEngine2.SimplifyWithRule;
    }

    public double relativeQuality() {
      return isSpecialRule() ? 0 : doubleRatio(qualitySum(), nPossibleVars());
    }

    public Set<String> possibleVars() {
      return asCISet(codeTokens(tokenize(first(rule.in))));
    }

    public RuleEngine2.Rule applyMapping() {
      RuleEngine2.Rule r = new RuleEngine2.Rule();
      r.in = map(rule.in, new F1<String, String>() {

        public String get(String x) {
          try {
            return applyMappingTo(x);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "applyMappingTo(x)";
        }
      });
      r.out = applyMappingTo(rule.out);
      return r;
    }

    public String appliedRuleText() {
      return applyMapping().asText();
    }

    public String outText() {
      if (out == null)
        out = join(ai_mapCodeTokensWithMap(outTokenize(rule.out), map));
      return out;
    }

    public String applyMappingTo(GRuleLine line) {
      return line == null ? null : join(ai_mapCodeTokensWithMap(tokenize(line), map));
    }

    public String applyMappingTo(String s) {
      return s == null ? null : join(ai_mapCodeTokensWithMap(tokenize(s), map));
    }

    public List<String> tokenize(String s) {
      return (List<String>) callAndMake(assertNotNull(tokenizationFunction), s);
    }

    public List<String> outTokenize(String s) {
      return (List<String>) callAndMake(assertNotNull(or(outTokenizer, tokenizationFunction)), s);
    }

    public List<String> tokenize(GRuleLine line) {
      return (List<String>) callAndMake(assertNotNull(or(line.tokenizer, tokenizationFunction)), line.text);
    }

    public boolean moreConditions() {
      return rule != null && iCond < l(rule.in);
    }

    public String ruleID() {
      return rule.globalID;
    }

    public void flush() {
      if (tokenizationFunction != null || outTokenizer != null)
        out = null;
    }

    public String toString() {
      return "MatchedRule " + ruleID() + " with " + map + (moreConditions() ? " [unsatisfied]" : "");
    }
  }

  public static class GRuleLine extends TextAndType {

    public String tokenizer;

    public List<String> comments;

    public GRuleLine() {
    }

    public GRuleLine(String text, String type) {
      this.type = type;
      this.text = text;
    }

    public boolean equals(Object o) {
      return stdEq2(this, o);
    }

    public int hashCode() {
      return stdHash2(this);
    }
  }

  public static class JavaXClassLoader extends URLClassLoader {

    public String progID;

    public List<File> files;

    public Set<String> triedToLoad = synchroSet();

    public Set<Class> loadedClasses = synchroSet();

    public boolean retired;

    public Object retiredMarker;

    public JavaXClassLoader(String progID, List<File> files) {
      this(progID, files, getSystemClassLoader());
    }

    public JavaXClassLoader(String progID, List<File> files, ClassLoader parent) {
      super(new URL[0], parent);
      this.progID = progID;
      this.files = files;
      {
        try {
          for (File f : files) addURL(f.toURI().toURL());
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }
      fixACCInClassLoader(this);
    }

    public Class<?> findClass(String name) throws ClassNotFoundException {
      boolean triedBefore = !triedToLoad.add(name);
      try {
        Class<?> c = super.findClass(name);
        loadedClasses.add(c);
        if (eq(name, "main"))
          callOpt(javax(), "registerAMainClass", c);
        return c;
      } catch (ClassNotFoundException e) {
        throw new ClassNotFoundException("Class " + name + " not found in " + joinWithComma(map("f2s", files)) + " (progID=" + progID + ")" + (triedBefore ? ", tried to load before" : ""), e);
      }
    }

    public String toString() {
      return "JavaXClassLoader[" + systemHashCodeHex(this) + "] - " + progID;
    }
  }

  public abstract static class DialogIO {

    public String line;

    public boolean eos, loud, noClose;

    public Lock lock = lock();

    public abstract String readLineImpl();

    public abstract boolean isStillConnected();

    public abstract void sendLine(String line);

    public abstract boolean isLocalConnection();

    public abstract Socket getSocket();

    public abstract void close();

    public int getPort() {
      Socket s = getSocket();
      return s == null ? 0 : s.getPort();
    }

    public boolean helloRead;

    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;
    }
  }

  public abstract static class DialogHandler {

    public abstract void run(DialogIO io);
  }

  public abstract static class IterableIterator<A> implements Iterator<A>, Iterable<A> {

    public Iterator<A> iterator() {
      return this;
    }

    public void remove() {
      unsupportedOperation();
    }
  }

  public abstract static class F2<A, B, C> {

    public abstract C get(A a, B b);
  }

  public abstract static class RandomAccessAbstractList<A> extends AbstractList<A> implements RandomAccess {
  }

  public static class Flag {

    public boolean up;

    public synchronized boolean raise() {
      if (!up) {
        up = true;
        notifyAll();
        return true;
      } else
        return false;
    }

    public synchronized void waitUntilUp() {
      while (!up) {
        try {
          wait();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }

    public synchronized void waitUntilUp(long timeout) {
      if (!up) {
        try {
          wait(timeout);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }

    public synchronized boolean isUp() {
      return up;
    }

    public boolean get() {
      return isUp();
    }

    public String toString() {
      return isUp() ? "up" : "down";
    }

    public void waitForThisOr(Flag otherFlag) {
      try {
        while (!isUp() && !otherFlag.isUp()) Thread.sleep(50);
      } catch (Exception __e) {
        throw rethrow(__e);
      }
    }
  }

  public static 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 abstract static class CloseableIterableIterator<A> extends IterableIterator<A> implements AutoCloseable {

    public void close() throws Exception {
    }
  }

  public static class ExpiringMap2<A, B> extends AbstractMap<A, B> {

    public Map<A, Pair<Long, B>> byKey = new HashMap();

    public PriorityBlockingQueue<Pair<Long, A>> queue = new PriorityBlockingQueue();

    public long standardExpiryTime;

    public boolean renewOnOverwrite = true, renewOnGet;

    public Object onChange;

    public ExpiringMap2() {
    }

    public ExpiringMap2(long standardExpiryTime) {
      this.standardExpiryTime = standardExpiryTime;
    }

    public ExpiringMap2(long standardExpiryTime, Object onChange) {
      this.onChange = onChange;
      this.standardExpiryTime = standardExpiryTime;
    }

    public synchronized boolean clean() {
      boolean changes = false;
      Pair<Long, A> p;
      while ((p = queue.peek()) != null && sysTime() >= p.a) {
        p = queue.poll();
        Pair<Long, B> v = byKey.get(p.b);
        if (v != null) {
          byKey.remove(p.b);
          changes = true;
          change();
        }
      }
      return changes;
    }

    public void change() {
      callF(onChange);
    }

    public synchronized B put(A a, B b) {
      clean();
      long timeout = sysTime() + standardExpiryTime;
      Pair<Long, B> p = byKey.get(a);
      if (p != null && renewOnOverwrite)
        queue.remove(new Pair(p.a, a));
      byKey.put(a, pair(timeout, b));
      change();
      if (p == null || renewOnOverwrite)
        queue.add(new Pair(timeout, a));
      return pairB(p);
    }

    public synchronized B remove(Object a) {
      clean();
      Pair<Long, B> p = byKey.get(a);
      if (p == null)
        return null;
      queue.remove(new Pair(p.a, a));
      byKey.remove(a);
      change();
      return p.b;
    }

    public synchronized B get(Object a) {
      clean();
      Pair<Long, B> p = byKey.get(a);
      if (renewOnGet && p != null) {
        queue.remove(new Pair(p.a, a));
        long timeout = sysTime() + standardExpiryTime;
        byKey.put((A) a, pair(timeout, p.b));
        queue.add(new Pair(timeout, a));
      }
      return pairB(p);
    }

    public synchronized Set<Map.Entry<A, B>> entrySet() {
      clean();
      return synchronizedSet(mapValues("pairB", byKey).entrySet());
    }

    public synchronized Set<A> keySet() {
      clean();
      return synchronizedSet(byKey.keySet());
    }

    public synchronized int size() {
      clean();
      return byKey.size();
    }

    public void setStandardExpiryTime(long ms) {
      standardExpiryTime = ms;
    }

    public synchronized ExpiringMap2<A, B> setMap(Map innerMap) {
      byKey = innerMap;
      return this;
    }
  }

  public static class JavaXClassLoaderWithParent2 extends JavaXClassLoader {

    public ClassLoader virtualParent;

    public List<String> classesToSkip;

    public JavaXClassLoaderWithParent2(String progID, List<File> files, ClassLoader virtualParent, List<String> classesToSkip) {
      super(progID, files);
      this.virtualParent = virtualParent;
      this.classesToSkip = classesToSkip;
    }

    public Class<?> findClass(String name) throws ClassNotFoundException {
      if (shouldDelegate(name)) {
        Class<?> c = virtualParent.loadClass(name);
        if (c != null)
          return c;
      }
      return super.findClass(name);
    }

    public boolean shouldDelegate(String name) {
      for (String s : classesToSkip) if (eq(name, s) || startsWith(name, s + "$"))
        return true;
      return false;
    }
  }

  public static class PosNegScore {

    public static String _fieldOrder = "pos neg";

    public double pos;

    public double neg;

    public PosNegScore() {
    }

    public PosNegScore(double pos, double neg) {
      this.neg = neg;
      this.pos = pos;
    }

    public String toString() {
      return "PosNegScore(" + pos + ", " + neg + ")";
    }

    public boolean equals(Object o) {
      if (!(o instanceof PosNegScore))
        return false;
      PosNegScore x = (PosNegScore) o;
      return eq(pos, x.pos) && eq(neg, x.neg);
    }

    public int hashCode() {
      int h = 1208109366;
      h = h * 2 + _hashCode(pos);
      h = h * 2 + _hashCode(neg);
      return h;
    }
  }

  public static interface Producer<A> {

    public A next();
  }

  public static interface IF1<A, B> {

    public B get(A a);
  }

  public abstract static class DynPrintLogAndEnabled extends DynPrintLog {

    public volatile boolean enabled = true;

    public transient JPanel buttons;

    public JComponent visualize() {
      return centerAndSouthWithMargins(super.visualize(), buttons = jrightalignedline(dm_fieldCheckBox("enabled")));
    }
  }

  public static class MechMode {

    public Object readMode;

    public Object writeMode;

    public MechMode() {
    }

    public MechMode(Object readMode, Object writeMode) {
      this.writeMode = writeMode;
      this.readMode = readMode;
    }

    public String toString() {
      return "MechMode(" + readMode + ", " + writeMode + ")";
    }

    public boolean equals(Object o) {
      if (!(o instanceof MechMode))
        return false;
      MechMode x = (MechMode) o;
      return eq(readMode, x.readMode) && eq(writeMode, x.writeMode);
    }

    public int hashCode() {
      int h = -866263200;
      h = h * 2 + _hashCode(readMode);
      h = h * 2 + _hashCode(writeMode);
      return h;
    }
  }

  public static interface IVar<A> {

    public void set(A a);

    public A get();

    public boolean has();

    public void clear();
  }

  public static class Cache<A> {

    public Object maker;

    public A value;

    public long loaded;

    public static boolean debug;

    public long changeCount;

    public Lock lock = lock();

    public Cache() {
    }

    public Cache(Object maker) {
      this.maker = maker;
    }

    public A get() {
      if (hasLock(lock))
        return value;
      Lock __2552 = lock;
      lock(__2552);
      try {
        if (loaded == 0) {
          value = make();
          changeCount++;
          loaded = sysNow();
        }
        return value;
      } finally {
        unlock(__2552);
      }
    }

    public void clear() {
      Lock __2553 = lock;
      lock(__2553);
      try {
        if (debug && loaded != 0)
          print("Clearing cache");
        value = null;
        changeCount++;
        loaded = 0;
      } finally {
        unlock(__2553);
      }
    }

    public void clear(double seconds) {
      Lock __2554 = lock;
      lock(__2554);
      try {
        if (seconds != 0 && loaded != 0 && sysNow() >= loaded + seconds * 1000)
          clear();
      } finally {
        unlock(__2554);
      }
    }

    public void set(A a) {
      Lock __2555 = lock;
      lock(__2555);
      try {
        value = a;
        ++changeCount;
        loaded = sysNow();
      } finally {
        unlock(__2555);
      }
    }

    public A make() {
      return (A) callF(maker);
    }
  }

  public static <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);
  }

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

  public static Field makeAccessible(Field f) {
    f.setAccessible(true);
    return f;
  }

  public static Method makeAccessible(Method m) {
    m.setAccessible(true);
    return m;
  }

  public static Set<Class> allInterfacesImplementedBy(Class c) {
    if (c == null)
      return null;
    HashSet<Class> set = new HashSet();
    allInterfacesImplementedBy_find(c, set);
    return set;
  }

  public static 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);
  }

  public static boolean call_checkArgs(Method m, Object[] args, boolean debug) {
    Class<?>[] types = m.getParameterTypes();
    if (types.length != args.length) {
      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;
  }

  public static 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;
  }

  public static 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;
  }

  public static String dropTrailingBackslashR(String s) {
    int i = l(s);
    while (i > 0 && s.charAt(i - 1) == '\r') --i;
    return substring(s, 0, i);
  }

  public static void gazelle_addSharedClasses() {
    hotwire_addSharedClasses(RuleEngine2.class, GazelleTree.class, GazelleEvalContext.class, GInterpretable.class, Pair.class);
  }

  public static String appendSquareBracketed(String b) {
    return empty(b) ? "" : "" + " [" + b + "]";
  }

  public static String appendSquareBracketed(String a, String b) {
    return empty(b) ? a : a + appendSquareBracketed(b);
  }

  public static List<String> listPlusNempties(Collection<String> l, String... more) {
    return concatLists_conservative(l, nempties(asList(more)));
  }

  public static String formatDouble(double d, int digits) {
    String format = digits <= 0 ? "0" : "0." + rep(digits, '#');
    return new java.text.DecimalFormat(format, new java.text.DecimalFormatSymbols(Locale.ENGLISH)).format(d);
  }

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

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

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

  public static void _add(BitSet bs, int i) {
    add(bs, i);
  }

  public static <A> boolean _add(Collection<A> c, A a) {
    return add(c, a);
  }

  public static void _add(Container c, Component x) {
    add(c, x);
  }

  public static boolean hasLock(Lock lock) {
    return ((ReentrantLock) lock).isHeldByCurrentThread();
  }

  public static String mL_raw(String name) {
    return mechList_raw(name);
  }

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

  public static <A, B> Map<A, B> putAll(Map<A, B> a, Map<? extends A, ? extends B> b) {
    if (a != null)
      a.putAll(b);
    return a;
  }

  public static <A, B> Set<A> keySet(Map<A, B> map) {
    return map == null ? new HashSet() : map.keySet();
  }

  public static Set keySet(Object map) {
    return keys((Map) map);
  }

  public static <A> Set<A> keySet(MultiSet<A> ms) {
    return ms.keySet();
  }

  public static <A, B> Set<A> keySet(MultiMap<A, B> mm) {
    return mm.keySet();
  }

  public static <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;
  }

  public static void gazelle_parseInstruction(RuleEngine2.SimplifyWithRule r) {
    Matches m = new Matches();
    String s = r.text;
    if (match("simplify with *", s, m)) {
      r._function = m.unq(0);
      return;
    } else if (match("simplify with * on tree", s, m)) {
      r.callOnTree = true;
      {
        r._function = m.unq(0);
        return;
      }
    } else if (match("drop after word *", s, m)) {
      final String word = m.unq(0);
      {
        r._function = new F1<String, Object>() {

          public Object get(String s) {
            try {
              return tok_dropStartingWithWord(word, s);
            } catch (Exception __e) {
              throw rethrow(__e);
            }
          }

          public String toString() {
            return "tok_dropStartingWithWord(word, s)";
          }
        };
        return;
      }
    } else if (match("group words *", s, m)) {
      final String words = m.unq(0);
      {
        r._function = new F1<String, Object>() {

          public Object get(String s) {
            try {
              return tok_groupPhrase(s, words);
            } catch (Exception __e) {
              throw rethrow(__e);
            }
          }

          public String toString() {
            return "tok_groupPhrase(s, words)";
          }
        };
        return;
      }
    } else if (match("split with *", s, m)) {
      r.isSplitRule = true;
      {
        r._function = m.unq(0);
        return;
      }
    }
  }

  public static <A> List<A> addToOrCreateList(List<A> l, A a) {
    return addDyn(l, a);
  }

  public static <A> List<A> listSetWithCreate(List<A> l, int i, A a) {
    if (l == null)
      l = new ArrayList();
    listSet(l, i, a);
    return l;
  }

  public static Pair<String, String> ai_rule_pair_expandPlusArrow(Pair<String, String> rule) {
    if (endsWith(pairA(rule), "+"))
      return pair(rule.a + " " + rule.b, rule.b);
    return rule;
  }

  public static boolean jMatchStart(String pat, String s) {
    return jMatchStart(pat, s, null);
  }

  public static boolean jMatchStart(String pat, String s, Matches matches) {
    if (s == null)
      return false;
    return jMatchStart(pat, javaTok(s), matches);
  }

  public static boolean jMatchStart(String pat, List<String> toks, Matches matches) {
    List<String> tokpat = jfind_preprocess(javaTok(pat));
    if (toks.size() < tokpat.size())
      return false;
    String[] m = match2x(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] = join(toks.subList(tokpat.size(), toks.size()));
    }
    return true;
  }

  public static List<String> tok_groupLastTokensToMatchPattern(List<String> tok, List<String> tokPat) {
    return concatLists(subList(tok, 0, l(tokPat) - 2), ll(curly(joinSubList(tok, l(tokPat) - 2, l(tok) - 1)), last(tok)));
  }

  public static Set<String> ai_wordsInBothSidesOfPair_uncurly(Pair<String, String> p) {
    return mapCISet("uncurly", ai_wordsInBothSidesOfPair(p));
  }

  public static <A> A firstWhere(Collection<A> c, Object... data) {
    return findWhere(c, data);
  }

  public static int _hashCode(Object a) {
    return a == null ? 0 : a.hashCode();
  }

  public static String b(Object contents, Object... params) {
    return tag("b", contents, params);
  }

  public static <A, B> NavigableSet<A> navigableKeys(NavigableMap<A, B> map) {
    return map == null ? new TreeSet() : map.navigableKeySet();
  }

  public static <A> NavigableSet<A> navigableKeys(MultiSet<A> ms) {
    return ((NavigableMap) ms.map).navigableKeySet();
  }

  public static <A, B> NavigableSet<A> navigableKeys(MultiMap<A, B> mm) {
    return ((NavigableMap) mm.data).navigableKeySet();
  }

  public static void fill(RGBImage img, String col) {
    RGB rgb = new RGB(col);
    int w = img.getWidth(), h = img.getHeight();
    for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) img.setPixel(x, y, rgb);
  }

  public static Q dm_q() {
    return dm_current_mandatory().q();
  }

  public static void dm_q(Runnable r) {
    dm_inQ(r);
  }

  public static void dm_q(DynModule module, Runnable r) {
    module.q().add(r);
  }

  public static Pair<String, String> dm_textAndCommentForRule(String ruleID) {
    return (Pair) quickImport(dm_call(dm_gazelle_rulesModule(), "textAndCommentForRule", ruleID));
  }

  public static RuleEngine2 gazelle_dummyEngineWithRule(RuleEngine2.Rule rule) {
    RuleEngine2 engine = new RuleEngine2();
    engine.rules.add(rule);
    return engine;
  }

  public static void gazelle_updateStatsForRule(GazelleEvalContext ctx, String ruleID) {
    MultiSet<String> statements = dm_gazelle_statementsForRule_multiSet(ruleID);
    ctx.dataByRule.put(ruleID, ai_gazelle_analyzeStatementsForRule_multiSet(statements));
  }

  public static boolean getServerTranspiled2_allowLocalFallback = true, getServerTranspiled2_localFallbackVerbose = true;

  public static String getServerTranspiled2(String id) {
    IResourceLoader rl = vm_getResourceLoader();
    if (rl != null)
      return rl.getTranspiled(id);
    String transpiled = loadCachedTranspilation(id);
    String md5 = null;
    if (machineIsOffline() || isOfflineMode() || isLocalSnippet(id))
      return transpiled;
    if (transpiled != null)
      md5 = md5(transpiled);
    String transpiledSrc;
    try {
      transpiledSrc = getServerTranspiled(formatSnippetID(id), md5);
    } catch (Throwable e) {
      if (!getServerTranspiled2_allowLocalFallback)
        rethrow(e);
      printExceptionShort(e);
      if (getServerTranspiled2_localFallbackVerbose)
        print("Fallback to local code");
      return transpiled;
    }
    if (eq(transpiledSrc, "SAME")) {
      if (!isTrue(loadPage_silent.get()))
        print("SAME");
      return transpiled;
    }
    return transpiledSrc;
  }

  public static List<String> findMainClass(List<String> tok) {
    for (List<String> c : reversedList(allClasses(tok))) {
      String name = getClassDeclarationName(c);
      if (eq(name, "main") || name.startsWith("x"))
        return c;
    }
    return findBlock("m {", tok);
  }

  public static boolean useDummyMainClasses() {
    return true;
  }

  public static boolean tok_classHasModifier(List<String> classDecl, String modifier) {
    if (classDecl == null)
      return false;
    int i = classDecl.indexOf("class");
    return subList(classDecl, 0, i).contains(modifier);
  }

  public static boolean javaCompileToJar_useRAMDisk;

  public static File javaCompileToJar_optionalRename(String src, File destJar, String progIDForRename) {
    return javaCompileToJar_optionalRename(src, "", destJar, progIDForRename);
  }

  public static synchronized File javaCompileToJar_optionalRename(String src, String dehlibs, File destJar, String progIDForRename) {
    return javaCompileToJar_optionalRename(src, dehlibs, destJar, progIDForRename, null);
  }

  public static synchronized File javaCompileToJar_optionalRename(String src, String dehlibs, File destJar, String progIDForRename, String progID) {
    String javaTarget = null;
    String dummyClass = "main";
    if (progIDForRename != null) {
      dummyClass = dummyMainClassName(progIDForRename);
      src += "\nclass " + dummyClass + "{}";
    }
    String md5 = md5(src);
    File jar = destJar;
    Class j = getJavaX();
    if (javaTarget != null)
      setOpt(j, "javaTarget", javaTarget);
    File srcDir = tempDir();
    String className = "main";
    String fileName = dummyClass + ".java";
    List<String> tok = javaTok(src);
    String packageName = tok_packageName(tok);
    if (packageName != null)
      fileName = packageName.replace(".", "/") + "/" + tok_firstClassName(tok) + ".java";
    File mainJava = new File(srcDir, fileName);
    saveTextFile(mainJava, src);
    File classesDir = javaCompileToJar_useRAMDisk ? tempDirPossiblyInRAMDisk() : tempDir();
    try {
      List<File> libraries = new ArrayList();
      Matcher m = Pattern.compile("\\d+").matcher(dehlibs);
      while (m.find()) {
        String libID = m.group();
        assertTrue(isSnippetID(libID));
        libraries.add(loadLibraryOrSrcLib(libID));
      }
      libraries.add(pathToJavaxJar());
      String compilerOutput;
      try {
        compilerOutput = (String) call(j, "compileJava", srcDir, libraries, classesDir);
      } catch (Throwable e) {
        compilerOutput = (String) get(getJavaX(), "javaCompilerOutput");
        String msg = cleanJavaCompilerOutput(compilerOutput);
        if (neqic(e.getMessage(), "Java compiler returned errors.") || empty(msg))
          msg = appendWithNewLine(msg, str(e));
        printStackTrace(e);
        throw fail(msg);
      }
      compilerOutput = cleanJavaCompilerOutput("Annotation processing got disabled, since it requires a 1.6 compliant JVM");
      if (nempty(compilerOutput)) {
        print("Compiler said: " + compilerOutput);
      }
      if (!anyFileWithExtensionInDir(classesDir, ".class"))
        throw fail("No classes generated");
      saveTextFile(new File(classesDir, "main.java"), src);
      if (nempty(dehlibs))
        saveTextFile(new File(classesDir, "libraries"), dehlibs);
      saveTextFile(new File(classesDir, "progID"), progID);
      dir2zip_recurse_verbose = false;
      int n = dir2zip_recurse(classesDir, jar);
      return jar;
    } finally {
      if (isInRAMDisk(classesDir))
        deleteDirectory(classesDir);
    }
  }

  public static void touchFile(File file) {
    try {
      closeRandomAccessFile(newRandomAccessFile(mkdirsForFile(file), "rw"));
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static File getCacheProgramDir() {
    return getCacheProgramDir(getProgramID());
  }

  public static File getCacheProgramDir(String snippetID) {
    return new File(userHome(), "JavaX-Caches/" + formatSnippetIDOpt(snippetID));
  }

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

  public static 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();
  }

  public static 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);
    }
  }

  public static int hashCodeFor(Object a) {
    return a == null ? 0 : a.hashCode();
  }

  public static String aGlobalID() {
    return randomID(globalIDLength());
  }

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

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

  public static double doubleRatio(double x, double y) {
    return y == 0 ? 0 : x / y;
  }

  public static List<String> ai_mapCodeTokensWithMap(List<String> tok, final Map<String, String> map) {
    return mapCodeTokens(tok, new F1<String, String>() {

      public String get(String s) {
        try {
          return getOrKeep_tryUncurlied(map, s);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "getOrKeep_tryUncurlied(map, s)";
      }
    });
  }

  public static 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;
  }

  public static int stdHash2(Object a) {
    if (a == null)
      return 0;
    return stdHash(a, toStringArray(allFields(a)));
  }

  public static void fixACCInClassLoader(Object o) {
    try {
      AccessControlContext acc = vm_globalACC();
      if (acc != null)
        replaceACCInClassLoader(o, acc);
    } catch (Throwable __e) {
      _handleException(__e);
    }
  }

  public static String systemHashCodeHex(Object o) {
    return intToHex(identityHashCode(o));
  }

  public static UnsupportedOperationException unsupportedOperation() {
    throw new UnsupportedOperationException();
  }

  public static long sysTime() {
    return sysNow();
  }

  public static void change() {
    callOpt(getOptMC("mainConcepts"), "allChanged");
  }

  public static JPanel centerAndSouthWithMargins(Component c, Component s) {
    return applyDefaultMargin(centerAndSouth(c, withTopMargin(s)));
  }

  public static JPanel jrightalignedline(Component... components) {
    return jrightAlignedLine(components);
  }

  public static JPanel jrightalignedline(List<Component> components) {
    return jrightAlignedLine(components);
  }

  public static JCheckBox dm_fieldCheckBox(String field) {
    return dm_fieldCheckBox(humanizeFormLabel(field), field);
  }

  public static JCheckBox dm_fieldCheckBox(String text, String field) {
    if (isIdentifier(text) && !isIdentifier(field)) {
      String temp = field;
      field = text;
      text = temp;
    }
    return jLiveValueCheckBox(text, dm_fieldLiveValue(field));
  }

  public static void hotwire_addSharedClasses(Collection<Class> classes) {
    addAll(hotwire_classesToShare, map("className", classes));
  }

  public static void hotwire_addSharedClasses(Class... classes) {
    hotwire_addSharedClasses(asList(classes));
  }

  public static <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);
  }

  public static <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);
  }

  public static List<String> nempties(Collection<String> c) {
    return filterNempty(c);
  }

  public static String rep(int n, char c) {
    return repeat(c, n);
  }

  public static String rep(char c, int n) {
    return repeat(c, n);
  }

  public static <A> List<A> rep(A a, int n) {
    return repeat(a, n);
  }

  public static <A> List<A> rep(int n, A a) {
    return repeat(n, a);
  }

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

  public static String tok_dropStartingWithWord(String word, String s) {
    List<String> tok = javaTokWithBrackets(s);
    int i = indexOfIC(tok, word);
    return i < 0 ? s : joinSubList(tok, 0, i - 1);
  }

  public static List<String> tok_groupPhrase(List<String> tok, String phrase) {
    List<String> tokPhrase = dropFirstAndLast(javaTok_cached(phrase));
    List<Integer> l = indicesOfSubListIC(tok, tokPhrase);
    for (int i : l) {
      int j = i + l(tokPhrase) - 1;
      if (i == 1 && j == l(tok) - 2)
        continue;
      String pre = get(tok, i - 1), post = get(tok, j + 1);
      if (endsWith(pre, "{") && startsWith(post, "}"))
        continue;
      tokPrepend(tok, i, "{");
      tokAppend(tok, j, "}");
    }
    return tok;
  }

  public static String tok_groupPhrase(String s, String phrase) {
    return join(tok_groupPhrase(javaTok(s), phrase));
  }

  public static <A> List<A> addDyn(List<A> l, A a) {
    if (l == null)
      l = new ArrayList();
    l.add(a);
    return l;
  }

  public static <A> void listSet(List<A> l, int i, A a, A emptyElement) {
    if (i < 0)
      return;
    while (i >= l(l)) l.add(emptyElement);
    l.set(i, a);
  }

  public static <A> void listSet(List<A> l, int i, A a) {
    listSet(l, i, a, null);
  }

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

  public static String[] match2x(List<String> pat, List<String> tok) {
    ArrayList<String> result = new ArrayList();
    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);
      boolean match;
      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, t))
          return null;
        continue;
      }
      if (!match)
        return null;
      result.add(t);
    }
    return result.toArray(new String[result.size()]);
  }

  public static TreeSet<String> mapCISet(Object f, Iterable l) {
    return mapToCISet(f, l);
  }

  public static Set<String> ai_wordsInBothSidesOfPair(Pair<String, String> p) {
    return filterCISet("hasLetterOrDigit", ciSetIntersection(javaTokWithBracketsPlusJavaTokC(p.a), javaTokWithBracketsPlusJavaTokC(p.b)));
  }

  public static <A> A findWhere(Collection<A> c, Object... data) {
    if (c != null)
      for (A x : c) if (checkFields(x, data))
        return x;
    return null;
  }

  public static String tag(String tag) {
    return htag(tag);
  }

  public static String tag(String tag, Object contents, Object... params) {
    return htag(tag, str(contents), params);
  }

  public static String tag(String tag, StringBuilder contents, Object... params) {
    return htag(tag, contents, params);
  }

  public static String tag(String tag, StringBuffer contents, Object... params) {
    return htag(tag, contents, params);
  }

  public static void dm_inQ(Runnable r) {
    dm_q().add(r);
  }

  public static MultiSet<String> dm_gazelle_statementsForRule_multiSet(String ruleID) {
    List concepts = cloneList((List) dm_requireAndCall("#1021413/AppliedRules", "feedbackForRule", ruleID));
    MultiSet<String> statements = new MultiSet();
    for (Object c : concepts) {
      try {
        String judgement = getString("judgement", c);
        String struct = getString("matchedRuleStruct", c);
        RuleEngine2_MatchedRule mr = (RuleEngine2_MatchedRule) (unstructureUnlessContainsAnonymousClasses(struct));
        if (mr == null)
          continue;
        statements.addAll(ai_gazelle_statementsFromAppliedRule(mr, judgement));
      } catch (Throwable __e) {
        _handleException(__e);
      }
    }
    return statements;
  }

  public static String loadCachedTranspilation(String id) {
    try {
      return loadTextFilePossiblyGZipped(getCachedTranspilationFile(id));
    } catch (Throwable __e) {
      return null;
    }
  }

  public static boolean machineIsOffline() {
    return isFalse(callF(vmGeneralMap_get("areWeOnline")));
  }

  public static boolean isOfflineMode() {
    return eq("1", trim(loadProgramTextFile("#1005806", "offline-mode")));
  }

  public static boolean isLocalSnippet(String snippetID) {
    return isLocalSnippetID(snippetID);
  }

  public static boolean isLocalSnippet(long snippetID) {
    return isLocalSnippetID(snippetID);
  }

  public static String getServerTranspiled(String snippetID) {
    return getServerTranspiled(snippetID, null);
  }

  public static String getServerTranspiled(String snippetID, String expectedMD5) {
    try {
      long id = parseSnippetID(snippetID);
      String text = loadPage_utf8(tb_mainServer() + "/tb-int/get-transpiled.php?raw=1&withlibs=1&id=" + id + "&utf8=1" + (l(expectedMD5) > 1 ? "&md5=" + urlencode(expectedMD5) : "") + standardCredentials());
      if (nempty(text) && neq(text, "SAME"))
        saveTranspiledCode(snippetID, text);
      return text;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static void printExceptionShort(Throwable e) {
    print(exceptionToStringShort(e));
  }

  public static HashSet<String> allClasses_keywords = lithashset("class", "interface", "enum", "sclass", "sinterface", "record", "srecord");

  public static List<List<String>> allClasses(List<String> tok) {
    List<List<String>> l = new ArrayList();
    int n = tok.size();
    HashSet<String> _allClasses_keywords = allClasses_keywords;
    for (int i = 1; i < n; i += 2) {
      String t = tok.get(i);
      if ("{".equals(t))
        i = findEndOfBlock(tok, i) - 1;
      else if (_allClasses_keywords.contains(t) && (i == 1 || !eqGetOneOf(tok, i - 2, ".", "include"))) {
        int j = i;
        while (j < n && !tok.get(j).equals("{")) j += 2;
        j = findEndOfBlock(tok, j) + 1;
        i = leftScanModifiers(tok, i);
        l.add(tok.subList(i - 1, Math.min(n, j)));
        i = j - 2;
      }
    }
    return l;
  }

  public static List<List<String>> allClasses(String text) {
    return allClasses(javaTok(text));
  }

  public static String getClassDeclarationName(List<String> tok) {
    if (tok != null)
      for (int i = 1; i + 2 < tok.size(); i += 2) if (allClasses_keywords.contains(tok.get(i)) && isIdentifier(tok.get(i + 2)))
        return tok.get(i + 2);
    return null;
  }

  public static List<String> findBlock(String pat, List<String> tok) {
    List<String> tokpat = javaTok(pat);
    int i = findCodeTokens(tok, toStringArray(codeTokensOnly(tokpat)));
    if (i < 0)
      return null;
    int bracketIdx = i + tokpat.size() - 3;
    assertEquals("{", tok.get(bracketIdx));
    int endIdx = findEndOfBlock(tok, bracketIdx);
    return subList(tok, i - 1, endIdx + 1);
  }

  public static String dummyMainClassName(String progID) {
    return "m" + psI(progID);
  }

  public static File tempDir() {
    return makeTempDir();
  }

  public static String tok_packageName(List<String> tok) {
    int i = jfind(tok, "package");
    if (i < 0)
      return "";
    i += 2;
    int j = jfind(tok, i, ";");
    if (j < 0)
      return "";
    return join(codeTokensOnly(subList(tok, i - 1, j)));
  }

  public static String tok_firstClassName(List<String> tok) {
    int i = jfind(tok, "class <id>");
    return i < 0 ? null : tok.get(i + 2);
  }

  public static File tempDirPossiblyInRAMDisk() {
    File f = linux_fileInRamDisk(aGlobalID());
    if (f != null) {
      f.mkdirs();
      return f;
    }
    return makeTempDir();
  }

  public static boolean loadLibraryOrSrcLib_srcLibsEnabled = true;

  public static File loadLibraryOrSrcLib(String snippetID) {
    try {
      long id = parseSnippetID(snippetID);
      boolean srcLib = loadLibraryOrSrcLib_srcLibsEnabled && isMarkedAsSrcLib(snippetID);
      if (srcLib)
        return pairA(hotwire_compile(snippetID));
      File f = DiskSnippetCache_getLibrary(id);
      if (fileSize(f) != 0)
        return f;
      try {
        return loadDataSnippetToFile(snippetID);
      } catch (Throwable e) {
        if (loadLibraryOrSrcLib_srcLibsEnabled && nempty(loadSnippet(snippetID))) {
          markAsSrcLib(snippetID);
          return pairA(hotwire_compile(snippetID));
        }
        throw rethrow(e);
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static File pathToJavaxJar() {
    try {
      IResourceLoader rl = vm_getResourceLoader();
      if (rl != null)
        return rl.pathToJavaXJar();
      int x = latestInstalledJavaX();
      File xfile = new File(userHome(), ".javax/x" + Math.max(x, 30) + ".jar");
      if (!xfile.isFile()) {
        print("Saving " + f2s(xfile));
        String url = "http://tinybrain.de/x30.jar";
        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);
    }
  }

  public static String cleanJavaCompilerOutput(String s) {
    return dropPrefixTrim("Annotation processing got disabled, since it requires a 1.6 compliant JVM", s);
  }

  public static boolean neqic(String a, String b) {
    return !eqic(a, b);
  }

  public static String appendWithNewLine(String a, String b) {
    if (empty(b))
      return a;
    if (empty(a))
      return b;
    return addSuffix(a, "\n") + b;
  }

  public static boolean anyFileWithExtensionInDir(File dir, String ext) {
    return nempty(filesWithExtension(ext, findAllFiles_noDirs(dir)));
  }

  public static boolean dir2zip_recurse_verbose;

  public static int dir2zip_recurse(File inDir, File zip) {
    return dir2zip_recurse(inDir, zip, "");
  }

  public static int dir2zip_recurse(File inDir, File zip, String outPrefix) {
    try {
      mkdirsForFile(zip);
      FileOutputStream fout = newFileOutputStream(zip);
      ZipOutputStream outZip = new ZipOutputStream(fout);
      try {
        return dir2zip_recurse(inDir, outZip, outPrefix, 0);
      } finally {
        outZip.close();
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static int dir2zip_recurse(File inDir, ZipOutputStream outZip) {
    return dir2zip_recurse(inDir, outZip, "", 0);
  }

  public static int dir2zip_recurse(File inDir, ZipOutputStream outZip, String outPrefix, int level) {
    try {
      if (++level >= 20)
        throw fail("woot? 20 levels in zip?");
      List<File> files = new ArrayList();
      for (File f : listFiles(inDir)) files.add(f);
      int n = 0;
      sortFilesByName(files);
      for (File f : files) {
        if (f.isDirectory()) {
          print("dir2zip_recurse: Scanning " + f.getAbsolutePath());
          n += dir2zip_recurse(f, outZip, outPrefix + f.getName() + "/", level);
        } else {
          if (dir2zip_recurse_verbose)
            print("Copying " + f.getName());
          outZip.putNextEntry(new ZipEntry(outPrefix + f.getName()));
          InputStream fin = new FileInputStream(f);
          copyStream(fin, outZip);
          fin.close();
          ++n;
        }
      }
      return n;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static boolean isInRAMDisk(File f) {
    return startsWith(f2s(f), "/dev/shm");
  }

  public static void deleteDirectory(File dir) {
    deleteDirectory(dir, false, false);
  }

  public static void deleteDirectory(File dir, boolean verbose, boolean testRun) {
    deleteAllFilesInDirectory(dir, verbose, testRun);
    if (verbose)
      print((testRun ? "Would delete " : "Deleting ") + dir.getAbsolutePath());
    if (!testRun)
      dir.delete();
  }

  public static void closeRandomAccessFile(RandomAccessFile f) {
    if (f != null)
      try {
        f.close();
        callJavaX("dropIO", f);
      } catch (Throwable e) {
        printStackTrace(e);
      }
  }

  public static RandomAccessFile newRandomAccessFile(File path, String mode) throws IOException {
    boolean forWrite = mode.indexOf('w') >= 0;
    if (forWrite)
      mkdirsForFile(path);
    RandomAccessFile f = new RandomAccessFile(path, mode);
    callJavaX("registerIO", f, path, forWrite);
    return f;
  }

  public static int globalIDLength() {
    return 16;
  }

  public static List mapCodeTokens(Object f, List l) {
    List out = new ArrayList();
    for (int i = 0; i < l(l); i++) {
      Object o = l.get(i);
      out.add(odd(i) ? callF(f, o) : o);
    }
    return out;
  }

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

  public static String getOrKeep_tryUncurlied(Map<String, String> map, String a) {
    String v = map.get(a);
    if (v != null)
      return v;
    String b = uncurly(a);
    if (a != b && (v = map.get(b)) != null)
      return v;
    return a;
  }

  public static Set<String> allFields(Object o) {
    TreeSet<String> fields = new TreeSet();
    Class _c = _getClass(o);
    do {
      for (Field f : _c.getDeclaredFields()) fields.add(f.getName());
      _c = _c.getSuperclass();
    } while (_c != null);
    return fields;
  }

  public static int stdHash(Object a, String... fields) {
    if (a == null)
      return 0;
    int hash = getClassName(a).hashCode();
    for (String field : fields) hash = hash * 2 + hashCode(getOpt(a, field));
    return hash;
  }

  public static AccessControlContext vm_globalACC() {
    return (AccessControlContext) vm_generalMap_get("Global ACC");
  }

  public static void replaceACCInClassLoader(Object o, AccessControlContext newACC) {
    ClassLoader cl = getClassLoaderOrSame(o);
    if (cl instanceof URLClassLoader) {
      try {
        setOpt(cl, "acc", newACC);
        try {
          setOpt(getOpt(cl, "ucp"), "acc", newACC);
        } catch (Throwable e) {
          printShortException(e);
          if (java10OrHigher())
            if (addDefaultVMOption("--add-opens java.base/jdk.internal.loader=ALL-UNNAMED"))
              print("Please restart the OS");
        }
      } catch (Throwable __e) {
        _handleException(__e);
      }
    }
  }

  public static String intToHex(int i) {
    return bytesToHex(intToBytes(i));
  }

  public static Object getOptMC(String field) {
    return getOpt(mc(), field);
  }

  public static <A extends JComponent> A applyDefaultMargin(final A c) {
    if (c != null) {
      swing(new Runnable() {

        public void run() {
          try {
            c.setBorder(BorderFactory.createEmptyBorder(withMargin_defaultWidth, withMargin_defaultWidth, withMargin_defaultWidth, withMargin_defaultWidth));
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "c.setBorder(BorderFactory.createEmptyBorder(withMargin_defaultWidth, withMarg...";
        }
      });
    }
    return c;
  }

  public static JPanel centerAndSouth(final Component c, final Component s) {
    return swing(new F0<JPanel>() {

      public JPanel get() {
        try {
          JPanel panel = new JPanel(new BorderLayout());
          panel.add(BorderLayout.CENTER, wrap(c));
          if (s != null)
            panel.add(BorderLayout.SOUTH, wrap(s));
          return panel;
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "JPanel panel = new JPanel(new BorderLayout);\r\n    panel.add(BorderLayout.CENT...";
      }
    });
  }

  public static int withTopMargin_defaultWidth = 6;

  public static JPanel withTopMargin(Component c) {
    return withTopMargin(withTopMargin_defaultWidth, c);
  }

  public static JPanel withTopMargin(final int w, final Component c) {
    return swing(new F0<JPanel>() {

      public JPanel get() {
        try {
          JPanel p = new JPanel(new BorderLayout());
          p.setBorder(BorderFactory.createEmptyBorder(w, 0, 0, 0));
          p.add(c);
          return p;
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "JPanel p = new JPanel(new BorderLayout);\r\n    p.setBorder(BorderFactory.creat...";
      }
    });
  }

  public static JPanel jrightAlignedLine(final Component... components) {
    return swing(new F0<RightAlignedLine>() {

      public RightAlignedLine get() {
        try {
          return new RightAlignedLine(components);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "ret RightAlignedLine(components);";
      }
    });
  }

  public static JPanel jrightAlignedLine(List<? extends Component> components) {
    return jrightAlignedLine(asArray(Component.class, components));
  }

  public static Map<String, String> humanizeFormLabel_replacements = litmap("id", "ID", "md5", "MD5");

  public static String humanizeFormLabel(String s) {
    if (containsSpace(s))
      return s;
    return firstToUpper(joinWithSpace(replaceElementsUsingMap(splitCamelCase(s), humanizeFormLabel_replacements)).replace("I D", "ID"));
  }

  public static JCheckBox jLiveValueCheckBox(String text, final SimpleLiveValue<Boolean> lv) {
    final JCheckBox cb = jCheckBox(text);
    bindCheckBoxToLiveValue(cb, lv);
    return cb;
  }

  public static SimpleLiveValue dm_fieldLiveValue(String fieldName) {
    return dm_fieldLiveValue(dm_current_mandatory(), fieldName);
  }

  public static SimpleLiveValue dm_fieldLiveValue(final DynModule module, final String fieldName) {
    Lock __2827 = module.lock;
    lock(__2827);
    try {
      Class type = getFieldType(module, fieldName);
      final SimpleLiveValue value = new SimpleLiveValue(type, get(module, fieldName));
      module.onChange(new Runnable() {

        public void run() {
          try {
            Object o = get(module, fieldName);
            value.set(o);
            ;
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "ifdef dm_fieldLiveValue_debug\r\n      print(\"dm_fieldLiveValue: setting \" + fi...";
        }
      });
      value.onChange(new Runnable() {

        public void run() {
          try {
            module.setField(fieldName, value.get());
            ;
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "ifdef dm_fieldLiveValue_debug\r\n      print(\"dm_fieldLiveValue: setting 2 \" + ...";
        }
      });
      return value;
    } finally {
      unlock(__2827);
    }
  }

  public static List<String> filterNempty(Collection<String> c) {
    List<String> l = new ArrayList();
    for (String x : unnull(c)) if (nempty(x))
      l.add(x);
    return l;
  }

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

  public static Map<String, List<String>> javaTok_cached_cache = synchronizedMRUCache(100);

  public static List<String> javaTok_cached(String s) {
    List<String> tok = javaTok_cached_cache.get(s);
    if (tok == null)
      javaTok_cached_cache.put(s, tok = javaTok(s));
    return tok;
  }

  public static List<Integer> indicesOfSubListIC(List<String> x, List<String> y) {
    List<Integer> l = new ArrayList();
    int i = 0;
    while (true) {
      int j = indexOfSubListIC(x, y, i);
      if (j < 0)
        break;
      l.add(j);
      i = j + 1;
    }
    return l;
  }

  public static TreeSet<String> mapToCISet(Object f, Iterable l) {
    TreeSet<String> x = ciSet();
    if (l != null)
      for (Object o : l) addIfNotNull(x, (String) callF(f, o));
    return x;
  }

  public static Set<String> filterCISet(Iterable<String> c, Object pred) {
    Set<String> x = ciSet();
    if (c != null)
      for (String o : c) if (isTrue(callF(pred, o)))
        x.add(o);
    return x;
  }

  public static Set<String> filterCISet(Object pred, Iterable c) {
    return filterCISet(c, pred);
  }

  public static boolean hasLetterOrDigit(String s) {
    return containsLetterOrDigit(s);
  }

  public static Set<String> ciSetIntersection(Collection<String> a, Collection<String> b) {
    Set<String> set = ciSet();
    Set<String> bSet = asCISet(b);
    for (String x : a) if (bSet.contains(x))
      set.add(x);
    return set;
  }

  public static List<String> javaTokWithBracketsPlusJavaTokC(String s) {
    return concatLists(javaTokPlusBracketsC(s), javaTokC(s));
  }

  public static 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;
  }

  public static String htag(String tag) {
    return htag(tag, "");
  }

  public static String htag(String tag, Object contents, Object... params) {
    String openingTag = hopeningTag(tag, params);
    String s = str(contents);
    if (empty(s) && neqic(tag, "script"))
      return dropLast(openingTag) + "/>";
    return openingTag + s + "</" + tag + ">";
  }

  public static String loadTextFilePossiblyGZipped(String fileName) {
    return loadTextFilePossiblyGZipped(fileName, null);
  }

  public static String loadTextFilePossiblyGZipped(String fileName, String defaultContents) {
    File gz = new File(fileName + ".gz");
    return gz.exists() ? loadGZTextFile(gz) : loadTextFile(fileName, defaultContents);
  }

  public static String loadTextFilePossiblyGZipped(File fileName) {
    return loadTextFilePossiblyGZipped(fileName, null);
  }

  public static String loadTextFilePossiblyGZipped(File fileName, String defaultContents) {
    return loadTextFilePossiblyGZipped(fileName.getPath(), defaultContents);
  }

  public static File getCachedTranspilationFile(String id) {
    return newFile(getCodeProgramDir(id), "Transpilation");
  }

  public static Object vmGeneralMap_get(Object key) {
    return vm_generalMap_get(key);
  }

  public static String loadProgramTextFile(String name) {
    return loadTextFile(getProgramFile(name));
  }

  public static String loadProgramTextFile(String progID, String name) {
    return loadTextFile(getProgramFile(progID, name));
  }

  public static String loadProgramTextFile(String progID, String name, String defaultText) {
    return loadTextFile(getProgramFile(progID, name), defaultText);
  }

  public static void saveTranspiledCode(String progID, String code) {
    File dir = getCodeProgramDir(progID);
    new File(dir, "Transpilation").delete();
    saveGZTextFile(new File(dir, "Transpilation.gz"), code);
  }

  public static int leftScanModifiers(List<String> tok, int i) {
    List<String> mod = getJavaModifiers();
    while (i > 1 && mod.contains(tok.get(i - 2))) i -= 2;
    return i;
  }

  public static File makeTempDir() {
    return (File) call(getJavaX(), "TempDirMaker_make");
  }

  public static File linux_fileInRamDisk(String name) {
    if (!isLinux())
      return null;
    File dir = newFile("/dev/shm");
    if (dir.isDirectory())
      return newFile(dir, name);
    return null;
  }

  public static boolean isMarkedAsSrcLib(String snippetID) {
    if (snippetID == null)
      return false;
    IResourceLoader rl = vm_getResourceLoader();
    if (rl != null)
      return isJavaxCompilableSnippetType(rl.getSnippetType(snippetID));
    return fileExists(javaxCodeDir("srclibs/" + psI(snippetID)));
  }

  public static Object hotwire_onCompile;

  public static boolean hotwire_serially;

  public static Lock hotwire_overInternalBot_lock = lock();

  public static boolean hotwire_compileOnServer;

  public static Class<?> hotwire_overInternalBot(String progID) {
    return hotwire_overInternalBot(progID, "main");
  }

  public static Class<?> hotwire_overInternalBot(String progID, String mainClass) {
    try {
      Pair<File, String> p;
      try {
        p = hotwire_compile(progID);
      } catch (Throwable e) {
        throw rethrow("Error hotwiring " + progID, e);
      }
      File jar = p.a;
      assertTrue(jar.getAbsolutePath(), jar.isFile());
      List<File> files = hotwire_collectJars(jar);
      JavaXClassLoader classLoader = hotwire_makeClassLoader(files);
      classLoader.progID = progID;
      return hotwire_finish(classLoader, progID, p.b, mainClass);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static Pair<File, String> hotwire_compile(String progID) {
    Pair<File, String> p = hotwire_compileOnServer && !isLocalSnippetID(progID) ? compileSnippetThroughServer(progID) : CompilerBot.compileSnippet2(progID);
    Lock __4 = hotwire_serially ? hotwire_overInternalBot_lock : null;
    lock(__4);
    try {
      callF(hotwire_onCompile, p);
      return p;
    } finally {
      unlock(__4);
    }
  }

  public static void markAsSrcLib(String snippetID) {
    saveTextFile(javaxCodeDir("srclibs/" + psI(snippetID)), "");
  }

  public static int latestInstalledJavaX() {
    File[] files = new File(userHome(), ".javax").listFiles();
    int v = 0;
    if (files != null)
      for (File f : files) {
        Matcher m = Pattern.compile("x(\\d\\d\\d?)\\.jar").matcher(f.getName());
        if (m.matches())
          v = Math.max(v, Integer.parseInt(m.group(1)));
      }
    return v;
  }

  public static String dropPrefixTrim(String prefix, String s) {
    return trim(dropPrefix(prefix, s));
  }

  public static String addSuffix(String s, String suffix) {
    return s == null || s.endsWith(suffix) ? s : s + suffix;
  }

  public static List<File> filesWithExtension(String ext, List<File> files) {
    return filesEndingWith(files, addPrefixIfNotEmpty2(".", ext));
  }

  public static List<File> findAllFiles_noDirs(List dirs) {
    return findAllFiles_noDirs(asObjectArray(dirs));
  }

  public static List<File> findAllFiles_noDirs(Object... dirs) {
    List<File> l = new ArrayList();
    for (Object dir : dirs) {
      if (dir instanceof String && ((String) dir).endsWith("/.")) {
        for (File f : listFiles(dropSuffix("/.", (String) dir))) if (f.isFile())
          l.add(f);
      } else
        findAllFiles_noDirs_impl(toFile(dir), l);
    }
    return l;
  }

  public static void findAllFiles_noDirs_impl(File dir, List<File> l) {
    for (File f : listFiles(dir)) {
      if (f.isDirectory())
        findAllFiles_noDirs_impl(f, l);
      else
        l.add(f);
    }
  }

  public static File[] listFiles(File dir) {
    File[] files = dir.listFiles();
    return files == null ? new File[0] : files;
  }

  public static File[] listFiles(String dir) {
    return listFiles(new File(dir));
  }

  public static List<File> sortFilesByName(List<File> l) {
    sort(l, new Comparator<File>() {

      public int compare(File a, File b) {
        return stdcompare(a.getName(), b.getName());
      }
    });
    return l;
  }

  public static int deleteAllFilesInDirectory_minPathLength = 10;

  public static void deleteAllFilesInDirectory(File dir) {
    deleteAllFilesInDirectory(dir, false, false);
  }

  public static void deleteAllFilesInDirectory(File dir, boolean verbose, boolean testRun) {
    dir = getCanonicalFile(dir);
    assertTrue(f2s(dir), l(f2s(dir)) >= deleteAllFilesInDirectory_minPathLength);
    File[] files = dir.listFiles();
    if (files == null)
      return;
    for (File f : files) {
      if (!isSymLink(f) && f.isDirectory())
        deleteDirectory(f, verbose, testRun);
      else {
        if (verbose)
          print((testRun ? "Would delete " : "Deleting ") + f.getAbsolutePath());
        if (!testRun)
          f.delete();
      }
    }
  }

  public static Object callJavaX(String method, Object... args) {
    return callOpt(getJavaX(), method, args);
  }

  public static int hashCode(Object a) {
    return a == null ? 0 : a.hashCode();
  }

  public static ClassLoader getClassLoaderOrSame(Object o) {
    return o instanceof ClassLoader ? (ClassLoader) o : getClassLoader(o);
  }

  public static void printShortException(Throwable e) {
    print(exceptionToStringShort(e));
  }

  public static boolean java10OrHigher() {
    return parseFirstInt(javaVersion()) >= 10;
  }

  public static boolean addDefaultVMOption(String option) {
    String s = defaultVMArgs(), old = s;
    if (!s.contains(option))
      s = trim(s + " " + option);
    if (eq(old, s))
      return false;
    {
      setDefaultVMArgs(s);
      return true;
    }
  }

  public static byte[] intToBytes(int i) {
    return new byte[] { (byte) (i >>> 24), (byte) (i >>> 16), (byte) (i >>> 8), (byte) i };
  }

  public static Object[] asArray(List l) {
    return toObjectArray(l);
  }

  public static <A> A[] asArray(Class<A> type, List l) {
    return (A[]) l.toArray((Object[]) Array.newInstance(type, l.size()));
  }

  public static boolean containsSpace(String s) {
    return containsSpaces(s);
  }

  public static String firstToUpper(String s) {
    if (empty(s))
      return s;
    return Character.toUpperCase(s.charAt(0)) + s.substring(1);
  }

  public static <A> List<A> replaceElementsUsingMap(Iterable<A> l, final Map<A, A> map) {
    return map(l, new F1<A, A>() {

      public A get(A a) {
        try {
          return getOrKeep(map, a);
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "getOrKeep(map, a)";
      }
    });
  }

  public static List<String> splitCamelCase(String s) {
    return ai_splitCamelCase(s);
  }

  public static JCheckBox jCheckBox() {
    return swingNu(JCheckBox.class);
  }

  public static JCheckBox jCheckBox(boolean checked) {
    return swingNu(JCheckBox.class, "", checked);
  }

  public static JCheckBox jCheckBox(String text, boolean checked) {
    return swingNu(JCheckBox.class, text, checked);
  }

  public static JCheckBox jCheckBox(String text) {
    return swingNu(JCheckBox.class, text);
  }

  public static JCheckBox jCheckBox(String text, boolean checked, final Object onChange) {
    JCheckBox cb = jCheckBox(checked, onChange);
    cb.setText(text);
    return cb;
  }

  public static JCheckBox jCheckBox(boolean checked, final Object onChange) {
    final JCheckBox cb = jCheckBox(checked);
    cb.addChangeListener(new ChangeListener() {

      public void stateChanged(ChangeEvent e) {
        pcallF(onChange, cb.isSelected());
      }
    });
    return cb;
  }

  public static <A extends JCheckBox> A bindCheckBoxToLiveValue(final A cb, final SimpleLiveValue<Boolean> lv) {
    bindLiveValueListenerToComponent(cb, lv, new Runnable() {

      public void run() {
        try {
          setChecked(cb, isTrue(lv.get()));
          ;
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "ifdef bindCheckBoxToLiveValue_debug\r\n      print(\"bindCheckBoxToLiveValue: se...";
      }
    });
    onChange(cb, new Runnable() {

      public void run() {
        try {
          lv.set(isChecked(cb));
          ;
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "ifdef bindCheckBoxToLiveValue_debug\r\n      print(\"bindCheckBoxToLiveValue: se...";
      }
    });
    return cb;
  }

  public static Class getFieldType(Object o, String field) {
    return fieldType(o, field);
  }

  public static int indexOfSubListIC(List<String> x, List<String> y) {
    return indexOfSubListIC(x, y, 0);
  }

  public static int indexOfSubListIC(List<String> x, List<String> y, int i) {
    outer: for (; i + l(y) <= l(x); i++) {
      for (int j = 0; j < l(y); j++) if (neqic(x.get(i + j), y.get(j)))
        continue outer;
      return i;
    }
    return -1;
  }

  public static boolean containsLetterOrDigit(String s) {
    for (int i = 0; i < l(s); i++) if (isLetterOrDigit(s.charAt(i)))
      return true;
    return false;
  }

  public static List<String> javaTokPlusBracketsC(String s) {
    return codeTokens(javaTokPlusBrackets(s));
  }

  public static String hopeningTag(String tag, Map params) {
    return hopeningTag(tag, mapToParams(params));
  }

  public static String hopeningTag(String tag, Object... params) {
    StringBuilder buf = new StringBuilder();
    buf.append("<" + tag);
    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 (val == html_valueLessParam())
          buf.append(" " + name);
        else {
          String s = str(val);
          if (!empty(s))
            buf.append(" " + name + "=" + htmlQuote(s));
        }
      }
    }
    buf.append(">");
    return str(buf);
  }

  public static String loadGZTextFile(File file) {
    try {
      if (!file.isFile())
        return null;
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      InputStream fis = new FileInputStream(file);
      GZIPInputStream gis = newGZIPInputStream(fis);
      try {
        byte[] buffer = new byte[1024];
        int len;
        while ((len = gis.read(buffer)) != -1) {
          baos.write(buffer, 0, len);
        }
      } finally {
        fis.close();
      }
      baos.close();
      return fromUtf8(baos.toByteArray());
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static File getCodeProgramDir() {
    return getCodeProgramDir(getProgramID());
  }

  public static File getCodeProgramDir(String snippetID) {
    return new File(javaxCodeDir(), formatSnippetID(snippetID));
  }

  public static File getCodeProgramDir(long snippetID) {
    return getCodeProgramDir(formatSnippetID(snippetID));
  }

  public static void saveGZTextFile(File file, String contents) {
    try {
      File parentFile = file.getParentFile();
      if (parentFile != null)
        parentFile.mkdirs();
      String tempFileName = file.getPath() + "_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());
        GZIPOutputStream gos = new GZIPOutputStream(fileOutputStream);
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(gos, "UTF-8");
        PrintWriter printWriter = new PrintWriter(outputStreamWriter);
        printWriter.print(contents);
        printWriter.close();
        gos.close();
        fileOutputStream.close();
      }
      if (file.exists() && !file.delete())
        throw new IOException("Can't delete " + file.getPath());
      if (contents != null)
        if (!tempFile.renameTo(file))
          throw new IOException("Can't rename " + tempFile + " to " + file);
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static List<String> getJavaModifiers_list = litlist("static", "abstract", "public", "private", "protected", "final", "native", "volatile", "synchronized", "transient");

  public static List<String> getJavaModifiers() {
    return getJavaModifiers_list;
  }

  public static Cache<Boolean> isLinux_cache = new Cache("isLinux_load");

  public static boolean isLinux() {
    return isLinux_cache.get();
  }

  public static Boolean isLinux_load() {
    return !isWindows() && !isMac() && !isAndroid();
  }

  public static boolean isJavaxCompilableSnippetType(int type) {
    return isJavaxCompilableSnippetTypeExceptInclude(type) || type == javaxIncludeSnippetType();
  }

  public static File javaxCodeDir_dir;

  public static File javaxCodeDir() {
    return javaxCodeDir_dir != null ? javaxCodeDir_dir : new File(userHome(), "JavaX-Code");
  }

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

  public static List<File> hotwire_collectJars(File jar) {
    List<String> libIDs = hotwire_libraryIDsFromJar_deleteJarOnFail(jar);
    List<File> files = ll(jar);
    for (String libID : libIDs) files.add(loadLibraryOrSrcLib(libID));
    return files;
  }

  public static Class hotwire_finish(ClassLoader classLoader, String progID, String javaSource) {
    return hotwire_finish(classLoader, progID, javaSource, "main");
  }

  public static Class hotwire_finish(ClassLoader classLoader, String progID, String javaSource, String mainClass) {
    try {
      Class<?> theClass = classLoader.loadClass(mainClass);
      Class j = getJavaX();
      setOpt(theClass, "myJavaSource_code", javaSource);
      synchronized (j) {
        call(j, "setVars", theClass, progID);
        callOpt(j, "addInstance", progID, theClass);
      }
      hotwire_copyOver(theClass);
      return theClass;
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static Pair<File, String> compileSnippetThroughServer(String progID) {
    String transpiledSrc = getServerTranspiled2(progID);
    String md5 = md5(transpiledSrc + "\n" + progID);
    File jar = CompilerBot.getJarFile(md5);
    if (jar == null || jar.length() <= 22) {
      byte[] jarData = null;
      boolean dontLoad = false;
      IResourceLoader rl = vm_getResourceLoader();
      if (rl != null) {
        dontLoad = true;
        File jar2 = rl.getSnippetJar(progID, transpiledSrc);
        if (jar2 != null)
          return pair(jar2, transpiledSrc);
      }
      if (!dontLoad) {
        try {
          jarData = loadBinaryPage("http://www.botcompany.de/jar/" + psI(progID) + "?md5=" + md5(transpiledSrc));
        } catch (Throwable __e) {
          _handleException(__e);
        }
      }
      if (!isJAR(jarData)) {
        if (jarData != null) {
          print(bytesToHex(takeFirstOfByteArray(8, jarData)));
          print("fallback to CompilerBot: " + fromUtf8(takeFirstOfByteArray(80, jarData)));
        }
        return CompilerBot.compileSnippet2(progID);
      }
      saveBinaryFile(jar, jarData);
    }
    return pair(jar, transpiledSrc);
  }

  public static List<File> filesEndingWith(File dir, String suffix) {
    return listFilesWithSuffix(dir, suffix);
  }

  public static List<File> filesEndingWith(List<File> l, String suffix) {
    List<File> out = new ArrayList();
    for (File f : unnull(l)) if (!f.isDirectory() && (empty(suffix) || endsWithIgnoreCase(f.getName(), suffix)))
      out.add(f);
    return out;
  }

  public static Object[] asObjectArray(List l) {
    return toObjectArray(l);
  }

  public static File toFile(Object o) {
    if (o instanceof File)
      return (File) o;
    if (o instanceof String)
      return new File((String) o);
    throw fail("Not a file: " + o);
  }

  public static File getCanonicalFile(File f) {
    try {
      return f == null ? null : f.getCanonicalFile();
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static boolean isSymLink(File f) {
    return f != null && Files.isSymbolicLink(toPath(f));
  }

  public static ClassLoader getClassLoader(Object o) {
    return o == null ? null : _getClass(o).getClassLoader();
  }

  public static int parseFirstInt(String s) {
    return parseInt(jextract("<int>", s));
  }

  public static String javaVersion() {
    return System.getProperty("java.version");
  }

  public static String defaultVMArgs() {
    return javaxDefaultVMArgs();
  }

  public static void setDefaultVMArgs(String args) {
    String oldArgs = javaxDefaultVMArgs();
    args = trim(args);
    if (neq(unnull(oldArgs), unnull(args))) {
      print();
      print("Changing default VM arguments from");
      print("  " + oldArgs);
      print("to");
      print("  " + args);
      print();
      saveTextFile(javaxDataDir("default-vm-args"), nullIfEmpty(args));
    }
  }

  public static boolean containsSpaces(String s) {
    return indexOf(s, ' ') >= 0;
  }

  public static List<String> ai_splitCamelCase(String s) {
    int j = 0;
    List<String> l = new ArrayList();
    if (isAllUpperCase(s)) {
      l.add(s);
      return l;
    }
    for (int i = 0; i < l(s); i++) if (isUpperCaseLetter(s.charAt(i)) && i > j) {
      l.add(substring(s, j, i));
      j = i;
    }
    if (j < l(s))
      l.add(substring(s, j));
    return l;
  }

  public static <A extends JComponent> A bindLiveValueListenerToComponent(A component, final LiveValue lv, final Runnable listener) {
    if (lv != null)
      bindToComponent(component, new Runnable() {

        public void run() {
          try {
            lv.onChangeAndNow(listener);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "ifdef bindLiveValueListenerToComponent_debug\r\n          print(\"bindLiveValueL...";
        }
      }, new Runnable() {

        public void run() {
          try {
            lv.removeOnChangeListener(listener);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "lv.removeOnChangeListener(listener)";
        }
      });
    return component;
  }

  public static void setChecked(final JCheckBox checkBox, final boolean b) {
    if (checkBox != null) {
      swing(new Runnable() {

        public void run() {
          try {
            if (isChecked(checkBox) != b)
              checkBox.setSelected(b);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "if (isChecked(checkBox) != b)\r\n      checkBox.setSelected(b);";
        }
      });
    }
  }

  public static void onChange(JSpinner spinner, Object r) {
    spinner.addChangeListener(changeListener(r));
  }

  public static <A extends AbstractButton> A onChange(A b, Object r) {
    b.addItemListener(itemListener(r));
    return b;
  }

  public static void onChange(JTextComponent tc, Object r) {
    onUpdate(tc, r);
  }

  public static void onChange(final JSlider slider, final Object r) {
    {
      swing(new Runnable() {

        public void run() {
          try {
            slider.addChangeListener(changeListener(r));
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "slider.addChangeListener(changeListener(r));";
        }
      });
    }
  }

  public static void onChange(JComboBox cb, final Object r) {
    if (isEditableComboBox(cb))
      onChange(textFieldFromComboBox(cb), r);
    else
      onSelectedItem(cb, new VF1<String>() {

        public void get(String s) {
          try {
            callF(r);
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

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

  public static boolean isChecked(final JCheckBox checkBox) {
    return checkBox != null && (boolean) swing(new F0<Boolean>() {

      public Boolean get() {
        try {
          return checkBox.isSelected();
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "ret checkBox.isSelected();";
      }
    });
  }

  public static boolean isChecked(final JCheckBoxMenuItem mi) {
    return mi != null && (boolean) swing(new F0<Boolean>() {

      public Boolean get() {
        try {
          return mi.isSelected();
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "ret mi.isSelected();";
      }
    });
  }

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

  public static Object html_valueLessParam_cache;

  public static Object html_valueLessParam() {
    if (html_valueLessParam_cache == null)
      html_valueLessParam_cache = html_valueLessParam_load();
    return html_valueLessParam_cache;
  }

  public static Object html_valueLessParam_load() {
    return new Object();
  }

  public static String htmlQuote(String s) {
    return "\"" + htmlencode_forParams(s) + "\"";
  }

  public static boolean isWindows() {
    return System.getProperty("os.name").contains("Windows");
  }

  public static boolean isMac() {
    return System.getProperty("os.name").toLowerCase().contains("mac");
  }

  public static boolean isJavaxCompilableSnippetTypeExceptInclude(int type) {
    return isJavaxApplicationSnippetType(type) || isJavaxModuleSnippetType(type) || type == snippetType_dynModule();
  }

  public static int javaxIncludeSnippetType() {
    return 42;
  }

  public static List<String> hotwire_libraryIDsFromJar_deleteJarOnFail(File jar) {
    try {
      return hotwire_libraryIDsFromJar(jar);
    } catch (Throwable _e) {
      jar.delete();
      throw rethrow(_e);
    }
  }

  public static byte[] isJAR_magic = bytesFromHex("504B0304");

  public static boolean isJAR(byte[] data) {
    return byteArrayStartsWith(data, isJAR_magic);
  }

  public static boolean isJAR(File f) {
    return isJAR(loadBeginningOfBinaryFile(f, l(isJAR_magic)));
  }

  public static byte[] takeFirstOfByteArray(byte[] b, int n) {
    return subByteArray(b, 0, n);
  }

  public static byte[] takeFirstOfByteArray(int n, byte[] b) {
    return takeFirstOfByteArray(b, n);
  }

  public static 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;
  }

  public static Path toPath(File f) {
    return f == null ? null : f.toPath();
  }

  public static String jextract(String pat, String s) {
    return jextract(pat, javaTok(s));
  }

  public static String jextract(String pat, List<String> tok) {
    List<String> tokpat = javaTok(pat);
    jfind_preprocess(tokpat);
    int i = jfind(tok, tokpat);
    if (i < 0)
      return null;
    int j = i + l(tokpat) - 2;
    return joinSubList(tok, i, j);
  }

  public static String javaxDefaultVMArgs() {
    File fileNew, fileOld;
    String text = loadTextFile(fileNew = javaxDataDir("default-vm-args"));
    if (text == null) {
      text = loadTextFile(fileOld = programFile("#1005850", "default-vm-args"));
      if (text != null)
        moveFile(fileOld, fileNew);
    }
    return trim(unnull(text));
  }

  public static boolean isAllUpperCase(String s) {
    return hasLettersAllUpperCase(s);
  }

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

  public static ChangeListener changeListener(final Object r) {
    return new ChangeListener() {

      public void stateChanged(ChangeEvent e) {
        pcallF(r);
      }
    };
  }

  public static ItemListener itemListener(final Object r) {
    return new ItemListener() {

      public void itemStateChanged(ItemEvent e) {
        pcallF(r);
      }
    };
  }

  public static void onUpdate(JComponent c, final Object r) {
    if (c instanceof JTextComponent)
      ((JTextComponent) c).getDocument().addDocumentListener(new DocumentListener() {

        public void insertUpdate(DocumentEvent e) {
          call(r);
        }

        public void removeUpdate(DocumentEvent e) {
          call(r);
        }

        public void changedUpdate(DocumentEvent e) {
          call(r);
        }
      });
    else if (c instanceof ItemSelectable)
      ((ItemSelectable) c).addItemListener(new ItemListener() {

        public void itemStateChanged(ItemEvent e) {
          call(r);
        }
      });
    else
      print("Warning: onUpdate doesn't know " + getClassName(c));
  }

  public static void onUpdate(List<? extends JComponent> l, Object r) {
    for (JComponent c : l) onUpdate(c, r);
  }

  public static boolean isEditableComboBox(final JComboBox cb) {
    return cb != null && swing(new F0<Boolean>() {

      public Boolean get() {
        try {
          return cb.isEditable();
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "ret cb.isEditable();";
      }
    });
  }

  public static JTextField textFieldFromComboBox(JComboBox cb) {
    return (JTextField) cb.getEditor().getEditorComponent();
  }

  public static JComboBox onSelectedItem(final JComboBox cb, final VF1<String> f) {
    addActionListener(cb, new Runnable() {

      public void run() {
        try {
          pcallF(f, selectedItem(cb));
        } catch (Exception __e) {
          throw rethrow(__e);
        }
      }

      public String toString() {
        return "pcallF(f, selectedItem(cb))";
      }
    });
    return cb;
  }

  public static String htmlencode_forParams(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 == '>') {
        out.append("&#");
        out.append((int) c);
        out.append(';');
      } else
        out.append(c);
    }
    return out.toString();
  }

  public static boolean isJavaxApplicationSnippetType(int type) {
    return type == snippetType_javaxSource() || type == snippetType_JavaXDesktop();
  }

  public static boolean isJavaxModuleSnippetType(int type) {
    return type == snippetType_javaxModule() || type == snippetType_javaxDesktopModule();
  }

  public static int snippetType_dynModule() {
    return 57;
  }

  public static List<String> hotwire_libraryIDsFromJar(File jar) {
    String dehlibs = unnull(loadTextFileFromZip(jar, "libraries"));
    return regexpExtractAll("\\d+", dehlibs);
  }

  public static boolean byteArrayStartsWith(byte[] a, byte[] b) {
    if (a == null || b == null)
      return false;
    if (a.length < b.length)
      return false;
    for (int i = 0; i < b.length; i++) if (a[i] != b[i])
      return false;
    return true;
  }

  public static byte[] loadBeginningOfBinaryFile(File file, int maxBytes) {
    return loadBinaryFilePart(file, 0, maxBytes);
  }

  public static byte[] subByteArray(byte[] b, int start) {
    return subByteArray(b, start, l(b));
  }

  public static 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;
  }

  public static void moveFile(File a, File b) {
    if (!renameFile(a, b))
      throw fail("File move failed: " + a + " to " + b);
  }

  public static boolean hasLettersAllUpperCase(String s) {
    return hasLetters(s) && !containsLowerCase(s);
  }

  public static void addActionListener(JTextField tf, final Runnable action) {
    onEnter(tf, action);
  }

  public static void addActionListener(final JComboBox cb, final Runnable action) {
    if (cb != null) {
      swing(new Runnable() {

        public void run() {
          try {
            cb.addActionListener(actionListener(action));
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "cb.addActionListener(actionListener(action));";
        }
      });
    }
  }

  public static void addActionListener(final AbstractButton b, final Runnable action) {
    if (b != null) {
      swing(new Runnable() {

        public void run() {
          try {
            b.addActionListener(actionListener(action));
          } catch (Exception __e) {
            throw rethrow(__e);
          }
        }

        public String toString() {
          return "b.addActionListener(actionListener(action));";
        }
      });
    }
  }

  public static String selectedItem(JList l) {
    return getSelectedItem(l);
  }

  public static String selectedItem(JComboBox cb) {
    return getSelectedItem(cb);
  }

  public static int snippetType_javaxSource() {
    return 34;
  }

  public static int snippetType_JavaXDesktop() {
    return 55;
  }

  public static int snippetType_javaxModule() {
    return 54;
  }

  public static int snippetType_javaxDesktopModule() {
    return 58;
  }

  public static String loadTextFileFromZip(File inZip, String fileName) {
    return loadTextFileFromZipFile(inZip, fileName);
  }

  public static List<String> regexpExtractAll(String pat, String s) {
    if (s == null)
      return null;
    Matcher m = regexpMatcher(pat, s);
    List<String> out = new ArrayList();
    while (m.find()) out.add(m.group());
    return out;
  }

  public static byte[] loadBinaryFilePart(File file, long start, long end) {
    try {
      RandomAccessFile raf = new RandomAccessFile(file, "r");
      int n = toInt(min(raf.length(), end - start));
      byte[] buffer = new byte[n];
      try {
        raf.seek(start);
        raf.readFully(buffer, 0, n);
        return buffer;
      } finally {
        raf.close();
      }
    } catch (Exception __e) {
      throw rethrow(__e);
    }
  }

  public static boolean renameFile(File a, File b) {
    mkdirsForFile(b);
    return a.renameTo(b);
  }

  public static boolean renameFile(File a, String newName) {
    return renameFile(a, fileInSameDir(a, newName));
  }

  public static boolean hasLetters(String s) {
    for (int i = 0; i < s.length(); i++) if (Character.isLetter(s.charAt(i)))
      return true;
    return false;
  }

  public static boolean containsLowerCase(String s) {
    for (int i = 0; i < l(s); i++) if (isLowerCase(s.charAt(i)))
      return true;
    return false;
  }

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

  public static class SimpleLiveValue<A> extends LiveValue<A> {

    public Class<A> type;

    public volatile A value;

    public transient List onChange = synchroList();

    public SimpleLiveValue(Class<A> type) {
      this.type = type;
    }

    public SimpleLiveValue(Class<A> type, A value) {
      this.value = value;
      this.type = type;
    }

    public Class<A> getType() {
      return type;
    }

    public A get() {
      return value;
    }

    public void onChange(Runnable l) {
      onChange.add(l);
    }

    public void onChangeAndNow(Runnable l) {
      onChange(l);
      callF(l);
    }

    public void removeOnChangeListener(Runnable l) {
      onChange.remove(l);
    }

    public void fireChanged() {
      pcallFAll(onChange);
    }

    public void set(A a) {
      if (neq(value, a)) {
        value = a;
        fireChanged();
      }
    }
  }

  public abstract static class LiveValue<A> {

    public abstract Class<A> getType();

    public abstract A get();

    public abstract void onChange(Runnable l);

    public abstract void removeOnChangeListener(Runnable l);

    public void onChangeAndNow(Runnable l) {
      onChange(l);
      callF(l);
    }
  }

  public static class RightAlignedLine extends JPanel {

    public RightAlignedLine(Component... components) {
      setLayout(LetterLayout.rightAlignedRow());
      for (Component component : components) add(component);
    }

    public void add(String text) {
      add(new JLabel(text));
    }
  }

  public static class LetterLayout implements LayoutManager {

    public String[] lines;

    public Map<String, Component> map = new TreeMap<String, Component>();

    public RC[] rows;

    public RC[] cols;

    public Cell[][] cells;

    public int spacingX = 10, spacingY = 10;

    public int insetTop, insetBottom, insetLeft, insetRight;

    public int template;

    public boolean formWideLeftSide, formWideRightSide;

    public static final int STALACTITE = 1, LEFT_ALIGNED_ROW = 2, CENTERED_ROW = 3, FORM = 4, RIGHT_ALIGNED_ROW = 5;

    public boolean debug;

    public void setLeftBorder(int border) {
      insetLeft = border;
    }

    public void setRightBorder(int border) {
      insetRight = border;
    }

    public static JComponent withBorder(JComponent component, int border) {
      JPanel panel = new JPanel(new LetterLayout("C").setBorder(border));
      panel.add("C", component);
      return panel;
    }

    public static JPanel panel(String... lines) {
      return new JPanel(new LetterLayout(lines));
    }

    public static JPanel stalactitePanel() {
      return new JPanel(stalactite());
    }

    public static class DummyComponent extends JComponent {
    }

    public static class Cell {

      public boolean aux;

      public int minWidth, minHeight;

      public Component component;

      public int colspan, rowspan;

      public double weightX, weightY;
    }

    public static class RC {

      public int min;

      public double weightSum;

      public int start;

      public int minEnd;
    }

    public LetterLayout(int template) {
      this.template = template;
    }

    public LetterLayout(String... lines) {
      this.lines = lines;
    }

    public void removeLayoutComponent(Component component) {
      map.values().remove(component);
    }

    public void layoutContainer(Container container) {
      prepareLayout(container);
      if (debug)
        System.out.println("Container size: " + container.getSize());
      Insets insets = getInsets(container);
      for (int r = 0; r < rows.length; r++) {
        for (int i = 0; i < cols.length; ) {
          Cell cell = cells[i][r];
          if (cell.aux)
            ++i;
          else {
            if (cell.component != null) {
              int x1 = cols[i].start;
              int y1 = rows[r].start;
              int x2 = i + cell.colspan < cols.length ? cols[i + cell.colspan].start - spacingX : container.getWidth() - insets.right;
              int y2 = r + cell.rowspan < rows.length ? rows[r + cell.rowspan].start - spacingY : container.getHeight() - insets.bottom;
              if (debug)
                System.out.println("Layouting (" + i + ", " + r + ", " + cell.component.getClass().getName() + "): " + x1 + " " + y1 + " " + x2 + " " + y2);
              cell.component.setBounds(x1, y1, x2 - x1, y2 - y1);
            }
            i += cells[i][r].colspan;
          }
        }
      }
    }

    public final void prepareLayout(Container container) {
      applyTemplate(container);
      int numRows = lines.length, numCols = lines[0].length();
      for (int i = 1; i < numRows; i++) if (lines[i].length() != numCols)
        throw new IllegalArgumentException("Lines have varying length");
      cells = new Cell[numCols][numRows];
      rows = new RC[numRows];
      cols = new RC[numCols];
      for (int r = 0; r < numRows; r++) rows[r] = new RC();
      for (int i = 0; i < numCols; i++) cols[i] = new RC();
      for (int r = 0; r < numRows; r++) for (int i = 0; i < numCols; i++) cells[i][r] = new Cell();
      for (int r = 0; r < numRows; r++) {
        String line = lines[r];
        for (int i = 0; i < numCols; ) {
          Cell cell = cells[i][r];
          if (cell.aux) {
            ++i;
            continue;
          }
          char ch = line.charAt(i);
          int iNext = i;
          do ++iNext; while (iNext < numCols && ch == line.charAt(iNext));
          int rNext = r;
          do ++rNext; while (rNext < numRows && ch == lines[rNext].charAt(i));
          cell.weightX = numCols == 1 || iNext > i + 1 ? 1.0 : 0.0;
          cell.weightY = numRows == 1 || rNext > r + 1 ? 1.0 : 0.0;
          Component c = map.get(String.valueOf(ch));
          cell.component = c;
          if (c != null) {
            cell.minWidth = c.getMinimumSize().width + spacingX;
            cell.minHeight = getMinimumHeight(c) + spacingY;
          }
          cell.colspan = iNext - i;
          cell.rowspan = rNext - r;
          if (cell.colspan == 1)
            cols[i].min = Math.max(cols[i].min, cell.minWidth);
          if (cell.rowspan == 1)
            rows[r].min = Math.max(rows[r].min, cell.minHeight);
          for (int r2 = r; r2 < rNext; r2++) for (int i2 = i; i2 < iNext; i2++) if (r2 != r || i2 != i)
            cells[i2][r2].aux = true;
          i = iNext;
        }
      }
      while (true) {
        for (int i = 0; i < numCols; i++) {
          int minStart = i == 0 ? 0 : cols[i - 1].minEnd;
          double weightStart = i == 0 ? 0.0 : cols[i - 1].weightSum;
          for (int r = 0; r < numRows; r++) {
            Cell cell = cells[i][r];
            if (!cell.aux) {
              RC rc = cols[i + cell.colspan - 1];
              rc.minEnd = Math.max(rc.minEnd, minStart + cell.minWidth);
              rc.weightSum = Math.max(rc.weightSum, weightStart + cell.weightX);
            }
          }
        }
        for (int r = 0; r < numRows; r++) {
          int minStart = r == 0 ? 0 : rows[r - 1].minEnd;
          double weightStart = r == 0 ? 0.0 : rows[r - 1].weightSum;
          for (int i = 0; i < numCols; i++) {
            Cell cell = cells[i][r];
            if (!cell.aux) {
              RC rc = rows[r + cell.rowspan - 1];
              rc.minEnd = Math.max(rc.minEnd, minStart + cell.minHeight);
              rc.weightSum = Math.max(rc.weightSum, weightStart + cell.weightY);
            }
          }
        }
        if (allWeightsZero(cols)) {
          for (int r = 0; r < numRows; r++) for (int i = 0; i < numCols; i++) cells[i][r].weightX = 1.0;
          continue;
        }
        if (allWeightsZero(rows)) {
          for (int r = 0; r < numRows; r++) for (int i = 0; i < numCols; i++) cells[i][r].weightY = 1.0;
          continue;
        }
        break;
      }
      Insets insets = getInsets(container);
      determineStarts(cols, insets.left, container.getWidth() - insets.left - insets.right + spacingX, spacingX);
      determineStarts(rows, insets.top, container.getHeight() - insets.top - insets.bottom + spacingY, spacingY);
    }

    public final boolean allWeightsZero(RC[] rcs) {
      for (int i = 0; i < rcs.length; i++) if (rcs[i].weightSum != 0.0)
        return false;
      return true;
    }

    public static int getMinimumHeight(Component c) {
      return c.getMinimumSize().height;
    }

    public final void applyTemplate(Container container) {
      if (template == STALACTITE) {
        Component[] components = container.getComponents();
        lines = new String[components.length + 2];
        map.clear();
        for (int i = 0; i < components.length; i++) {
          String s = String.valueOf(makeIndexChar(i));
          map.put(s, components[i]);
          lines[i] = s;
        }
        lines[components.length] = lines[components.length + 1] = " ";
      } else if (template == FORM) {
        Component[] components = container.getComponents();
        int numRows = components.length / 2;
        lines = new String[numRows + 2];
        map.clear();
        for (int row = 0; row < numRows; row++) {
          String lower = String.valueOf(makeIndexChar(row));
          String upper = String.valueOf(makeAlternateIndexChar(row));
          Component rightComponent = components[row * 2 + 1];
          if (rightComponent instanceof DummyComponent)
            upper = lower;
          lines[row] = (formWideLeftSide ? lower + lower : lower) + (formWideRightSide ? upper + upper : upper);
          map.put(lower, components[row * 2]);
          if (!(rightComponent instanceof DummyComponent))
            map.put(upper, rightComponent);
        }
        lines[numRows] = lines[numRows + 1] = (formWideLeftSide ? "  " : " ") + (formWideRightSide ? "  " : " ");
      } else if (template == LEFT_ALIGNED_ROW) {
        lines = new String[] { makeSingleRow(container) + RIGHT_CHAR + RIGHT_CHAR };
      } else if (template == CENTERED_ROW) {
        lines = new String[] { "" + LEFT_CHAR + LEFT_CHAR + makeSingleRow(container) + RIGHT_CHAR + RIGHT_CHAR };
      } else if (template == RIGHT_ALIGNED_ROW) {
        lines = new String[] { "" + LEFT_CHAR + LEFT_CHAR + makeSingleRow(container) };
      }
    }

    public final String makeSingleRow(Container container) {
      Component[] components = container.getComponents();
      StringBuffer buf = new StringBuffer();
      map.clear();
      for (int i = 0; i < components.length; i++) {
        String s = String.valueOf(makeAlternateIndexChar(i));
        map.put(s, components[i]);
        buf.append(s);
      }
      return buf.toString();
    }

    public static void determineStarts(RC[] rcs, int start, int totalSize, int spacing) {
      int minTotal = rcs[rcs.length - 1].minEnd;
      double weightSum = rcs[rcs.length - 1].weightSum;
      int spare = (int) ((totalSize - minTotal) / (weightSum == 0.0 ? 1.0 : weightSum));
      int x = start, minSum = 0;
      double prevWeightSum = 0.0;
      for (int i = 0; i < rcs.length; i++) {
        int width = rcs[i].minEnd - minSum + (int) ((rcs[i].weightSum - prevWeightSum) * spare) - spacing;
        rcs[i].start = x;
        x += width + spacing;
        prevWeightSum = rcs[i].weightSum;
        minSum = rcs[i].minEnd;
      }
    }

    public void addLayoutComponent(String s, Component component) {
      map.put(s, component);
    }

    public Dimension minimumLayoutSize(Container container) {
      prepareLayout(container);
      Insets insets = getInsets(container);
      Dimension result = new Dimension(insets.left + cols[cols.length - 1].minEnd + insets.right - spacingX, insets.top + rows[rows.length - 1].minEnd + insets.bottom - spacingY);
      return result;
    }

    public final Insets getInsets(Container container) {
      Insets insets = container.getInsets();
      return new Insets(insets.top + insetTop, insets.left + insetLeft, insets.bottom + insetBottom, insets.right + insetRight);
    }

    public Dimension preferredLayoutSize(Container container) {
      return minimumLayoutSize(container);
    }

    public LetterLayout setSpacing(int x, int y) {
      spacingX = x;
      spacingY = y;
      return this;
    }

    public LetterLayout setSpacing(int spacing) {
      return setSpacing(spacing, spacing);
    }

    public LetterLayout setBorder(int top, int left, int bottom, int right) {
      insetTop = top;
      insetLeft = left;
      insetBottom = bottom;
      insetRight = right;
      return this;
    }

    public LetterLayout setBorder(int inset) {
      return setBorder(inset, inset, inset, inset);
    }

    public LetterLayout setTopBorder(int inset) {
      insetTop = inset;
      return this;
    }

    public static LetterLayout stalactite() {
      return new LetterLayout(STALACTITE);
    }

    public static LetterLayout leftAlignedRow() {
      return new LetterLayout(LEFT_ALIGNED_ROW);
    }

    public static LetterLayout leftAlignedRow(int spacing) {
      return leftAlignedRow().setSpacing(spacing);
    }

    public static LetterLayout centeredRow() {
      return new LetterLayout(CENTERED_ROW);
    }

    public static LetterLayout rightAlignedRow() {
      return new LetterLayout(RIGHT_ALIGNED_ROW);
    }

    public static JPanel rightAlignedRowPanel(JComponent... components) {
      return makePanel(new LetterLayout(RIGHT_ALIGNED_ROW), components);
    }

    public static JPanel makePanel(LetterLayout letterLayout, JComponent[] components) {
      JPanel panel = new JPanel(letterLayout);
      for (JComponent component : components) {
        panel.add(component);
      }
      return panel;
    }

    public static LetterLayout form() {
      LetterLayout letterLayout = new LetterLayout(FORM);
      letterLayout.formWideLeftSide = true;
      letterLayout.formWideRightSide = true;
      return letterLayout;
    }

    public static LetterLayout formWideRightSide() {
      LetterLayout letterLayout = new LetterLayout(FORM);
      letterLayout.formWideRightSide = true;
      return letterLayout;
    }

    public static Component getDummyComponent() {
      return new DummyComponent();
    }

    public static JPanel newPanel(String... lines) {
      return new JPanel(new LetterLayout(lines));
    }

    public boolean isDebug() {
      return debug;
    }

    public void setDebug(boolean debug) {
      this.debug = debug;
    }

    public static char makeIndexChar(int idx) {
      return (char) ('a' + idx * 2);
    }

    public static char makeAlternateIndexChar(int idx) {
      return (char) ('b' + idx * 2);
    }

    public static char LEFT_CHAR = ',', RIGHT_CHAR = '.';

    public static void main(String[] args) {
      System.out.println((int) makeIndexChar(0));
      System.out.println((int) makeAlternateIndexChar(0));
      System.out.println((int) makeIndexChar(32000));
      System.out.println((int) makeAlternateIndexChar(32000));
      System.out.println((int) LEFT_CHAR);
      System.out.println((int) RIGHT_CHAR);
    }
  }
}