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.*;
// CompressionSearch
// The terms are flying all over the place with this one
// (compressor, decompressor, compression, decompression, ...)
// Basic idea: INPUT DATA (byte list) is compressed into a
// CUSTOM OBJECT (called the "COMPRESSION"), which, when run
// under a fitting COMPRESSION REGIME produces the INPUT DATA.
//
// Every compression can also be converted to and from a byte array
// (the compression regime does this).
import static x30_pkg.x30_util.DynamicObject;
class main {
static class CompressionSearch implements Steppable {
// user-set
CompressionRegime regime;
List inputData;
Steppable searcher; // main object that performs the search (optional)
// public output variables
Best best = new Best();
boolean searcherDone = false;
CompressionSearch(CompressionRegime regime) {
this.regime = regime;}
CompressionSearch(CompressionRegime regime, List inputData) {
this.inputData = inputData;
this.regime = regime;}
CompressionSearch(CompressionRegime regime, byte[] inputData) { this.inputData = byteArrayToList(inputData);
this.regime = regime; }
// can submit concurrently!
Submission submit(A compression) { return _submit(compression); }
// private (although Submission is returned)
class Submission {
public String toString() {
return linesLL(
"Compressor " + (correct() ? "correct" : "incorrect"),
"Compression factor " + score(),
"Code:",
indentx(str(decompressor())),
"Code as bytes:",
indentx(bytesToHex(compressed()))
);
}
void setDecompressor(A decompressor) {
this.decompressor_cache = decompressor;
}
List inputData() { return inputData; }
byte[] inputDataAsArray() { return byteListToArray(inputData); }
byte[] compressed_cache;
byte[] compressed() { if (compressed_cache == null) compressed_cache = compressed_load(); return compressed_cache; }
byte[] compressed_load() {
return decompressor_cache == null ? null : regime.decompressorToBytes(decompressor_cache);
}
A decompressor_cache;
A decompressor() { if (decompressor_cache == null) decompressor_cache = decompressor_load(); return decompressor_cache; }
A decompressor_load() {
return compressed_cache == null ? null : regime.decompressorFromBytes(compressed_cache);
}
int compressedSize() { return l(compressed()); }
Double score_cache;
double score() { if (score_cache == null) score_cache = score_load(); return score_cache; }
Double score_load() {
return doubleRatio(inputDataSize(), compressedSize());
}
boolean decompressed = false;
Object decompressedValue;
Object decompressed() {
if (!decompressed) {
decompressedValue = regime.runDecompressor(decompressor());
decompressed = true;
}
return decompressedValue;
}
Boolean correct_cache;
Boolean correct() { if (correct_cache == null) correct_cache = correct_load(); return correct_cache; }
Boolean correct_load() {
return eq(toByteList(decompressed()), inputData());
}
}
int inputDataSize() { return l(inputData); }
Submission _submit(A compression) {
if (compression == null) return null;
Submission s = new Submission();
s.setDecompressor(compression);
if (s.score() > best.score())
if (!s.correct())
warn("Compressor didn't verify");
else
best.put(s, s.score());
return s;
}
public boolean step() {
if (searcherDone || searcher == null) return false;
if (searcher.step()) return true;
searcherDone = true;
return false;
}
}
static ArrayList byteArrayToList(byte[] a) {
if (a == null) return null;
return byteArrayToList(a, 0, a.length);
}
// no range checking on from/to in the interest of speed
static ArrayList byteArrayToList(byte[] a, int from, int to) {
if (a == null) return null;
ArrayList < Byte > l = new ArrayList<>(to-from);
for (int i = from; i < to; i++) l.add(a[i]);
return l;
}
static String linesLL(Object... x) {
return lines(ll(x));
}
static float score(Scored s) {
return s == null ? 0 : s.score();
}
static String indentx(String s) {
return indentx(indent_default, s);
}
static String indentx(int n, String s) {
return dropSuffix(repeat(' ', n), indent(n, s));
}
static String indentx(String indent, String s) {
return dropSuffix(indent, indent(indent, s));
}
static String str(Object o) {
return o == null ? "null" : o.toString();
}
static String str(char[] c) {
return new String(c);
}
public static String bytesToHex(byte[] bytes) {
return bytesToHex(bytes, 0, bytes.length);
}
public static String bytesToHex(byte[] bytes, int ofs, int len) {
StringBuilder stringBuilder = new StringBuilder(len*2);
for (int i = 0; i < len; i++) {
String s = "0" + Integer.toHexString(bytes[ofs+i]);
stringBuilder.append(s.substring(s.length()-2, s.length()));
}
return stringBuilder.toString();
}
static byte[] byteListToArray(List l) {
if (l == null) return null;
int n = l(l), i = 0;
byte[] a = new byte[n];
for (var x : l)
a[i++] = x;
return a;
}
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 double doubleRatio(double x, double y) {
return y == 0 ? 0 : x/y;
}
static boolean eq(Object a, Object b) {
return a == b || a != null && b != null && a.equals(b);
}
// a little kludge for stuff like eq(symbol, "$X")
static boolean eq(Symbol a, String b) {
return eq(str(a), b);
}
static List toByteList(Object o) {
if (o == null) return null;
if (o instanceof List) return ((List) o);
if (o instanceof Iterable) return toList((Iterable) o);
if (o instanceof byte[]) return wrapPrimitiveArrayAsImmutableList((byte[]) o);
throw fail();
}
static boolean warn_on = true;
static ThreadLocal> warn_warnings = new ThreadLocal();
static void warn(String s) {
if (warn_on)
print("Warning: " + s);
}
static void warn(String s, List warnings) {
warn(s);
if (warnings != null)
warnings.add(s);
addToCollection(warn_warnings.get(), s);
}
static String lines(Iterable lines) { return fromLines(lines); }
static String lines(Object[] lines) { return fromLines(asList(lines)); }
static List lines(String s) { return toLines(s); }
// convenience map call
static String lines(Iterable l, IF1 f) {
return mapToLines(l, f);
}
static List ll(A... a) {
ArrayList l = new ArrayList(a.length);
if (a != null) for (A x : a) l.add(x);
return l;
}
static String dropSuffix(String suffix, String s) {
return nempty(suffix) && endsWith(s, suffix) ? s.substring(0, l(s)-l(suffix)) : s;
}
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);
}
static List repeat(A a, int n) {
n = Math.max(n, 0);
List l = new ArrayList(n);
for (int i = 0; i < n; i++)
l.add(a);
return l;
}
static List repeat(int n, A a) {
return repeat(a, n);
}
static int indent_default = 2;
static String indent(int indent) {
return repeat(' ', indent);
}
static String indent(int indent, String s) {
return indent(repeat(' ', indent), s);
}
static String indent(String indent, String s) {
return indent + s.replace("\n", "\n" + indent);
}
static String indent(String s) {
return indent(indent_default, s);
}
static List indent(String indent, List lines) {
List l = new ArrayList();
if (lines != null) for (String s : lines)
l.add(indent + s);
return l;
}
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);
}
static ArrayList toList(A[] a) { return asList(a); }
static ArrayList toList(int[] a) { return asList(a); }
static ArrayList toList(Set s) { return asList(s); }
static ArrayList toList(Iterable s) { return asList(s); }
static List wrapPrimitiveArrayAsImmutableList(Object array) {
int n = Array.getLength(array);
return new RandomAccessAbstractList() {
public int size() { return n; }
public Object get(int i) { return Array.get(array, i); }
};
}
static RuntimeException fail() { throw new RuntimeException("fail"); }
static RuntimeException fail(Throwable e) { throw asRuntimeException(e); }
static RuntimeException fail(Object msg) { throw new RuntimeException(String.valueOf(msg)); }
static RuntimeException fail(Object... objects) { throw new Fail(objects); }
static RuntimeException fail(String msg) { throw new RuntimeException(msg == null ? "" : msg); }
static RuntimeException fail(String msg, Throwable innerException) { throw new RuntimeException(msg, innerException); }
static 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