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 java.nio.file.Path;
import java.text.*;
import java.text.NumberFormat;
import java.nio.charset.Charset;
import java.util.TimeZone;
import java.text.SimpleDateFormat;
class main {
static A uniqPeer(Class c, Concept peer, Object... params) {
return uniq(peer.concepts(), c, params);
}
static A uniq(Class c, Object... params) {
return uniqueConcept(c, params);
}
static A uniq(Concepts cc, Class c, Object... params) {
return uniqueConcept(cc, c, params);
}
static A uniqueConcept(Class c, Object... params) {
return uniqueConcept(db_mainConcepts(), c, params);
}
static A uniqueConcept(Concepts cc, Class c, Object... params) {
AutoCloseable __1 = tempDBLock(cc); try {
params = expandParams(c, params);
A x = findConceptWhere(cc, c, params);
if (x == null) {
x = unlisted(c);
csetAll(x, params);
cc.register(x);
}
return x;
} finally { _close(__1); }}
static volatile Concepts mainConcepts; // Where we create new concepts
static Concepts db_mainConcepts() {
if (mainConcepts == null)
mainConcepts = newConceptsWithClassFinder(getDBProgramID());
return mainConcepts;
}
static void cleanMeUp_concepts() {
if (db_mainConcepts() != null) db_mainConcepts().cleanMeUp();
// mainConcepts = null; // TODO
}
static AutoCloseable tempDBLock(Concepts concepts) {
return tempLock(concepts.lock); // NO null propagation this time
}
static AutoCloseable tempDBLock() {
return tempDBLock(db_mainConcepts());
}
static Object[] expandParams(Class c, Object[] params) {
if (l(params) == 1)
params = new Object[] { singleFieldName(c), params[0] };
else
warnIfOddCount(params);
return params;
}
// TODO: if field is a Ref<>, you can thoretically use findBackRefs
static A findConceptWhere(Class c, Object... params) {
return findConceptWhere(db_mainConcepts(), c, params);
}
static A findConceptWhere(Concepts concepts, Class c, Object... params) {
params = expandParams(c, params);
// indexed
if (concepts.fieldIndices != null)
for (int i = 0; i < l(params); i += 2) {
IFieldIndex index = concepts.getFieldIndex(c, (String) params[i]);
if (index != null) {
for (A x : index.getAll(params[i+1]))
if (checkConceptFields(x, params)) return x;
return null;
}
}
// table scan
for (A x : concepts.list(c)) if (checkConceptFields(x, params)) return x;
return null;
}
static Concept findConceptWhere(Concepts concepts, String c, Object... params) {
for (Concept x : concepts.list(c)) if (checkConceptFields(x, params)) return x;
return null;
}
// make concept instance that is not connected to DB
static A unlisted(Class c, Object... args) {
concepts_unlisted.set(true);
try {
return nuObject(c, args);
} finally {
concepts_unlisted.set(null);
}
}
static Concept unlisted(String name, Object... args) {
Class extends Concept> cc = findClass(name);
concepts_unlisted.set(true);
try {
return cc != null ? nuObject(cc) : new Concept(name);
} finally {
concepts_unlisted.set(null);
}
}
static int csetAll(Concept c, Object... values) {
return cset(c, values);
}
static int csetAll(Iterable extends Concept> l, Object... values) {
int n = 0;
for (Concept c : unnull(l))
n += cset(c, values);
return n;
}
static void _close(AutoCloseable c) {
if (c != null) try {
c.close();
} catch (Throwable e) {
// Some classes stupidly throw an exception on double-closing
if (c instanceof javax.imageio.stream.ImageOutputStream)
return;
else throw rethrow(e);
}
}
static Concepts newConceptsWithClassFinder(String progID) {
Concepts cc = new Concepts(progID);
cc.classFinder = _defaultClassFinder();
return cc;
}
static String getDBProgramID_id;
static String getDBProgramID() {
return nempty(getDBProgramID_id) ? getDBProgramID_id : programIDWithCase();
}
// accept purpose argument so we are a drop-in for tempVerboseLock
static AutoCloseable tempLock(Lock lock) { return tempLock("", lock); }
static AutoCloseable tempLock(String purpose, Lock lock) {
if (lock == null) return null;
lock(lock);
return new AutoCloseable() { public String toString() { return "unlock(lock);"; } public void close() throws Exception { unlock(lock); }};
}
static int l(Object[] a) { return a == null ? 0 : a.length; }
static int l(boolean[] a) { return a == null ? 0 : a.length; }
static int l(byte[] a) { return a == null ? 0 : a.length; }
static int l(short[] a) { return a == null ? 0 : a.length; }
static int l(long[] a) { return a == null ? 0 : a.length; }
static int l(int[] a) { return a == null ? 0 : a.length; }
static int l(float[] a) { return a == null ? 0 : a.length; }
static int l(double[] a) { return a == null ? 0 : a.length; }
static int l(char[] a) { return a == null ? 0 : a.length; }
static int l(Collection c) { return c == null ? 0 : c.size(); }
static int l(Iterator i) { return iteratorCount_int_close(i); } // consumes the iterator && closes it if possible
static int l(Map m) { return m == null ? 0 : m.size(); }
static int l(CharSequence s) { return s == null ? 0 : s.length(); }
static long l(File f) { return f == null ? 0 : f.length(); }
static int l(Object o) {
return o == null ? 0
: o instanceof String ? l((String) o)
: o instanceof Map ? l((Map) o)
: o instanceof Collection ? l((Collection) o)
: o instanceof Object[] ? l((Object[]) o)
: o instanceof boolean[] ? l((boolean[]) o)
: o instanceof byte[] ? l((byte[]) o)
: o instanceof char[] ? l((char[]) o)
: o instanceof short[] ? l((short[]) o)
: o instanceof int[] ? l((int[]) o)
: o instanceof float[] ? l((float[]) o)
: o instanceof double[] ? l((double[]) o)
: o instanceof long[] ? l((long[]) o)
: (Integer) call(o, "size");
}
static int l(MultiSet ms) { return ms == null ? 0 : ms.size(); }
static String singleFieldName(Class c) {
Set l = listFields(c);
if (l(l) != 1)
throw fail("No single field found in " + c + " (have " + n(l(l), "fields") + ")");
return first(l);
}
static void warnIfOddCount(Object... list) {
if (odd(l(list)))
printStackTrace("Odd list size: " + list);
}
static boolean checkConceptFields(Concept x, Object... data) {
for (int i = 0; i < l(data); i += 2)
if (neq(cget(x, (String) data[i]), deref(data[i+1])))
return false;
return true;
}
static Object nuObject(String className, Object... args) { try {
return nuObject(classForName(className), args);
} catch (Exception __e) { throw rethrow(__e); } }
// too ambiguous - maybe need to fix some callers
/*static O nuObject(O realm, S className, O... args) {
ret nuObject(_getClass(realm, className), args);
}*/
static A nuObject(Class c, Object... args) { try {
if (args.length == 0) return nuObjectWithoutArguments(c); // cached!
Constructor m = nuObject_findConstructor(c, args);
makeAccessible(m);
return (A) m.newInstance(args);
} catch (Exception __e) { throw rethrow(__e); } }
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!" : ""));
}
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;
}
static HashMap findClass_cache = new HashMap();
// currently finds only inner classes of class "main"
// returns null on not found
// this is the simple version that is not case-tolerant
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;
}
}
// returns number of changes
static int cset(Concept c, Object... values) { try {
if (c == null) return 0;
warnIfOddCount(values = expandParams(c.getClass(), values));
int changes = 0;
for (int i = 0; i+1 < l(values); i += 2)
if (_csetField(c, (String) values[i], values[i+1])) ++changes;
return changes;
} catch (Exception __e) { throw rethrow(__e); } }
static int cset(Iterable extends Concept> l, Object... values) {
int changes = 0;
for (Concept c : unnull(l))
changes += cset(c, values);
return changes;
}
static int cset(Concept.Ref c, Object... values) {
return cset(getVar(c), values);
}
static String unnull(String s) {
return s == null ? "" : s;
}
static Collection unnull(Collection l) {
return l == null ? emptyList() : l;
}
static List unnull(List l) { return l == null ? emptyList() : l; }
static int[] unnull(int[] l) { return l == null ? emptyIntArray() : l; }
static char[] unnull(char[] l) { return l == null ? emptyCharArray() : l; }
static double[] unnull(double[] l) { return l == null ? emptyDoubleArray() : l; }
static Map unnull(Map l) {
return l == null ? emptyMap() : l;
}
static Iterable unnull(Iterable i) {
return i == null ? emptyList() : i;
}
static A[] unnull(A[] a) {
return a == null ? (A[]) emptyObjectArray() : a;
}
static BitSet unnull(BitSet b) {
return b == null ? new BitSet() : b;
}
//ifclass Symbol
static Pair unnull(Pair p) {
return p != null ? p : new Pair(null, null);
}
static long unnull(Long l) { return l == null ? 0L : l; }
static RuntimeException rethrow(Throwable t) {
if (t instanceof Error)
_handleError((Error) t);
throw t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t);
}
static RuntimeException rethrow(String msg, Throwable t) {
throw new RuntimeException(msg, t);
}
static Object _defaultClassFinder_value = defaultDefaultClassFinder();
static Object _defaultClassFinder() {
return _defaultClassFinder_value;
}
static boolean nempty(Collection c) {
return !empty(c);
}
static boolean nempty(CharSequence s) {
return !empty(s);
}
static boolean nempty(Object[] o) { return !empty(o); }
static boolean nempty(byte[] o) { return !empty(o); }
static boolean nempty(int[] o) { return !empty(o); }
static boolean nempty(Map m) {
return !empty(m);
}
static boolean nempty(Iterator i) {
return i != null && i.hasNext();
}
static boolean nempty(Object o) { return !empty(o); }
static String programIDWithCase() {
return nempty(caseID())
? programID() + "/" + quoteUnlessIdentifierOrInteger(caseID())
: programID();
}
static void lock(Lock lock) { try {
ping();
if (lock == null) return;
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
Object reason = vm_threadInterruptionReasonsMap().get(currentThread());
print("Locking interrupted! Reason: " + strOr(reason, "Unknown"));
printStackTrace(e);
rethrow(e);
}
// NO call to ping here! Make sure lock is always released.
} catch (Exception __e) { throw rethrow(__e); } }
static void lock(Lock lock, String msg) {
print("Locking: " + msg);
lock(lock);
}
static void lock(Lock lock, String msg, long timeout) {
print("Locking: " + msg);
lockOrFail(lock, timeout);
}
static ReentrantLock lock() {
return fairLock();
}
static void unlock(Lock lock, String msg) {
if (lock == null) return;
lock.unlock();
print("Unlocked: " + msg); // print afterwards to make sure the lock is always unlocked
}
static void unlock(Lock lock) {
if (lock == null) return;
lock.unlock();
}
static int iteratorCount_int_close(Iterator i) { try {
int n = 0;
if (i != null) while (i.hasNext()) { i.next(); ++n; }
if (i instanceof AutoCloseable) ((AutoCloseable) i).close();
return n;
} catch (Exception __e) { throw rethrow(__e); } }
static Object call(Object o) {
return callF(o);
}
// varargs assignment fixer for a single string array argument
static Object call(Object o, String method, String[] arg) {
return call(o, method, new Object[] {arg});
}
static Object call(Object o, String method, Object... args) {
//ret call_cached(o, method, args);
return call_withVarargs(o, method, args);
}
// This is for main classes that are all static.
// (We don't go to base classes.)
static Set listFields(Object c) {
TreeSet fields = new TreeSet();
for (Field f : _getClass(c).getDeclaredFields())
fields.add(f.getName());
return fields;
}
static RuntimeException fail() { throw new RuntimeException("fail"); }
static RuntimeException fail(Throwable e) { throw asRuntimeException(e); }
static RuntimeException fail(Object msg) { throw new RuntimeException(String.valueOf(msg)); }
static RuntimeException fail(String msg) { throw new RuntimeException(msg == null ? "" : msg); }
static RuntimeException fail(String msg, Throwable innerException) { throw new RuntimeException(msg, innerException); }
static String n(long l, String name) {
return l + " " + trim(l == 1 ? singular(name) : getPlural(name));
}
static String n(Collection l, String name) {
return n(l(l), name);
}
static String n(Map m, String name) {
return n(l(m), name);
}
static String n(Object[] a, String name) {
return n(l(a), name);
}
static String n(MultiSet ms, String name) {
return n(l(ms), name);
}
static Object first(Object list) {
return first((Iterable) list);
}
static A first(List list) {
return empty(list) ? null : list.get(0);
}
static A first(A[] bla) {
return bla == null || bla.length == 0 ? null : bla[0];
}
static A first(IterableIterator i) {
return first((Iterator) i);
}
static A first(Iterator i) {
return i == null || !i.hasNext() ? null : i.next();
}
static A first(Iterable i) {
if (i == null) return null;
Iterator it = i.iterator();
return it.hasNext() ? it.next() : null;
}
static Character first(String s) { return empty(s) ? null : s.charAt(0); }
static Character first(CharSequence s) { return empty(s) ? null : s.charAt(0); }
static A first(Pair p) {
return p == null ? null : p.a;
}
static Byte first(byte[] l) {
return empty(l) ? null : l[0];
}
static boolean odd(int i) {
return (i & 1) != 0;
}
static boolean odd(long i) {
return (i & 1) != 0;
}
static boolean odd(BigInteger i) { return odd(toInt(i)); }
static A printStackTrace(A e) {
// we go to system.out now - system.err is nonsense
print(getStackTrace(e));
return e;
}
static void printStackTrace() {
printStackTrace(new Throwable());
}
static void printStackTrace(String msg) {
printStackTrace(new Throwable(msg));
}
static void printStackTrace(String msg, Throwable e) {
printStackTrace(new Throwable(msg, e));
}
static boolean neq(Object a, Object b) {
return !eq(a, b);
}
// magic cast
static A cget(Object c, String field) {
Object o = getOpt(c, field);
if (o instanceof Concept.Ref) return (A) ((Concept.Ref) o).get();
return (A) o;
}
static A cget(String field, Object c) {
return cget(c, field);
}
static Object deref(Object o) {
if (o instanceof Derefable) o = ((Derefable) o).get();
return o;
}
static Map classForName_cache = synchroHashMap();
static Class classForName(String name) { try {
if (classForName_cache == null) return Class.forName(name); // in class init
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); } }
static Map nuObjectWithoutArguments_cache = newDangerousWeakHashMap();
static Object nuObjectWithoutArguments(String className) { try {
return nuObjectWithoutArguments(classForName(className));
} catch (Exception __e) { throw rethrow(__e); } }
static A nuObjectWithoutArguments(Class c) { try {
if (nuObjectWithoutArguments_cache == null)
// in class init
return (A) nuObjectWithoutArguments_findConstructor(c).newInstance();
Constructor m = nuObjectWithoutArguments_cache.get(c);
if (m == null)
nuObjectWithoutArguments_cache.put(c, m = nuObjectWithoutArguments_findConstructor(c));
return (A) m.newInstance();
} catch (Exception __e) { throw rethrow(__e); } }
static Constructor nuObjectWithoutArguments_findConstructor(Class c) {
for (Constructor m : c.getDeclaredConstructors())
if (empty(m.getParameterTypes())) {
makeAccessible(m);
return m;
}
throw fail("No default constructor found in " + c.getName());
}
static Field makeAccessible(Field f) {
try {
f.setAccessible(true);
} catch (Throwable e) {
// Note: The error reporting only works with Java VM option --illegal-access=deny
vmBus_send("makeAccessible_error",e, f);
}
return f;
}
static Method makeAccessible(Method m) {
try {
m.setAccessible(true);
} catch (Throwable e) {
vmBus_send("makeAccessible_error",e, m);
}
return m;
}
static Constructor makeAccessible(Constructor c) {
try {
c.setAccessible(true);
} catch (Throwable e) {
vmBus_send("makeAccessible_error",e, c);
}
return c;
}
static List getClasses(Object[] array) {
List l = emptyList(l(array));
for (Object o : array) l.add(_getClass(o));
return l;
}
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);
}
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;
}
// returns true if change
static boolean _csetField(Concept c, String field, Object value) { try {
Field f = setOpt_findField(c.getClass(), field);
//print("cset: " + c.id + " " + field + " " + struct(value) + " " + f);
if (value instanceof RC) value = c._concepts.getConcept((RC) value);
value = deref(value);
if (value instanceof String && l((String) value) >= concepts_internStringsLongerThan) value = intern((String) value);
if (f == null) {
// TODO: keep ref if it exists
c.fieldValues = syncMapPut2_createLinkedHashMap(c.fieldValues, assertIdentifier(field), value instanceof Concept ? c.new Ref((Concept) value) : value);
c.change();
} else if (isSubtypeOf(f.getType(), Concept.Ref.class)) {
// Concept.Ref magic
((Concept.Ref) f.get(c)).set((Concept) derefRef(value));
c.change(); return true;
} else if (isSubtypeOf(f.getType(), Concept.RefL.class)) {
// Concept.RefL magic
((Concept.RefL) f.get(c)).replaceWithList(lmap(__19 -> derefRef(__19),(List) value));
c.change(); return true;
} else {
Object old = f.get(c);
if (neq(value, old)) {
f.set(c, value);
if (!isTransient(f)) c.change();
return true;
}
}
return false;
} catch (Exception __e) { throw rethrow(__e); } }
static A getVar(IF0 v) {
return v == null ? null : v.get();
}
static ArrayList emptyList() {
return new ArrayList();
//ret Collections.emptyList();
}
static ArrayList emptyList(int capacity) {
return new ArrayList(max(0, capacity));
}
// Try to match capacity
static ArrayList emptyList(Iterable l) {
return l instanceof Collection ? emptyList(((Collection) l).size()) : emptyList();
}
static ArrayList emptyList(Object[] l) {
return emptyList(l(l));
}
// get correct type at once
static ArrayList emptyList(Class c) {
return new ArrayList();
}
static int[] emptyIntArray_a = new int[0];
static int[] emptyIntArray() { return emptyIntArray_a; }
static char[] emptyCharArray = new char[0];
static char[] emptyCharArray() { return emptyCharArray; }
static double[] emptyDoubleArray = new double[0];
static double[] emptyDoubleArray() { return emptyDoubleArray; }
static Map emptyMap() {
return new HashMap();
}
static Object[] emptyObjectArray_a = new Object[0];
static Object[] emptyObjectArray() { return emptyObjectArray_a; }
static void _handleError(Error e) {
call(javax(), "_handleError", e);
}
static Object defaultDefaultClassFinder() {
return new F1() {
public Class get(String name) {
Class c = findClass_fullName(name);
if (c != null) return c;
if (name.startsWith("loadableUtils.utils$"))
return findClass_fullName("main" + name.substring(19));
return null;
}
};
}
static boolean empty(Collection c) { return c == null || c.isEmpty(); }
static boolean empty(Iterable c) { return c == null || !c.iterator().hasNext(); }
static boolean empty(CharSequence s) { return s == null || s.length() == 0; }
static boolean empty(Map map) { return map == null || map.isEmpty(); }
static boolean empty(Object[] o) { return o == null || o.length == 0; }
static boolean empty(Object o) {
if (o instanceof Collection) return empty((Collection) o);
if (o instanceof String) return empty((String) o);
if (o instanceof Map) return empty((Map) o);
if (o instanceof Object[]) return empty((Object[]) o);
if (o instanceof byte[]) return empty((byte[]) o);
if (o == null) return true;
throw fail("unknown type for 'empty': " + getType(o));
}
static boolean empty(Iterator i) { return i == null || !i.hasNext(); }
static boolean empty(double[] a) { return a == null || a.length == 0; }
static boolean empty(float[] a) { return a == null || a.length == 0; }
static boolean empty(int[] a) { return a == null || a.length == 0; }
static boolean empty(long[] a) { return a == null || a.length == 0; }
static boolean empty(byte[] a) { return a == null || a.length == 0; }
static boolean empty(short[] a) { return a == null || a.length == 0; }
static boolean empty(MultiSet ms) { return ms == null || ms.isEmpty(); }
static boolean empty(File f) { return getFileSize(f) == 0; }
static volatile String caseID_caseID;
static String caseID() { return caseID_caseID; }
static void caseID(String id) {
caseID_caseID = id;
}
static String programID() {
return getProgramID();
}
static String programID(Object o) {
return getProgramID(o);
}
static String quoteUnlessIdentifierOrInteger(String s) {
return quoteIfNotIdentifierOrInteger(s);
}
//sbool ping_actions_shareable = true;
static volatile boolean ping_pauseAll = false;
static int ping_sleep = 100; // poll pauseAll flag every 100
static volatile boolean ping_anyActions = false;
static Map ping_actions = newWeakHashMap();
static ThreadLocal ping_isCleanUpThread = new ThreadLocal();
// always returns true
static boolean ping() {
if (ping_pauseAll || ping_anyActions) ping_impl(true /* XXX */);
//ifndef LeanMode ping_impl(); endifndef
return true;
}
// returns true when it slept
static boolean ping_impl(boolean okInCleanUp) { try {
if (ping_pauseAll && !isAWTThread()) {
do
Thread.sleep(ping_sleep);
while (ping_pauseAll);
return true;
}
if (ping_anyActions) { // don't allow sharing ping_actions
if (!okInCleanUp && !isTrue(ping_isCleanUpThread.get()))
failIfUnlicensed();
Object action = null;
synchronized(ping_actions) {
if (!ping_actions.isEmpty()) {
action = ping_actions.get(currentThread());
if (action instanceof Runnable)
ping_actions.remove(currentThread());
if (ping_actions.isEmpty()) ping_anyActions = false;
}
}
if (action instanceof Runnable)
((Runnable) action).run();
else if (eq(action, "cancelled"))
throw fail("Thread cancelled.");
}
return false;
} catch (Exception __e) { throw rethrow(__e); } }
static Map vm_threadInterruptionReasonsMap() {
return vm_generalWeakSubMap("Thread interruption reasons");
}
static Thread currentThread() {
return Thread.currentThread();
}
static volatile StringBuffer local_log = new StringBuffer(); // not redirected
static volatile Appendable print_log = local_log; // might be redirected, e.g. to main bot
// in bytes - will cut to half that
static volatile int print_log_max = 1024*1024;
static volatile int local_log_max = 100*1024;
static boolean print_silent = false; // total mute if set
static Object print_byThread_lock = new Object();
static volatile ThreadLocal