import java.util.*;
import java.util.zip.*;
import java.util.List;
import java.util.regex.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
import java.util.function.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.table.*;
import java.io.*;
import java.net.*;
import java.lang.reflect.*;
import java.lang.ref.*;
import java.lang.management.*;
import java.security.*;
import java.security.spec.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.imageio.*;
import java.math.*;
import java.time.Duration;
import java.lang.invoke.VarHandle;
import java.lang.invoke.MethodHandles;
// Abstract base class for tiling an image (G22Tiling)
import java.awt.geom.*;
import static x30_pkg.x30_util.DynamicObject;
import java.text.*;
import java.text.NumberFormat;
import java.util.TimeZone;
class main {
abstract static class G22AbstractTiler implements Runnable, Steppable , IFieldsToList{
Img image;
G22AbstractTiler() {}
G22AbstractTiler(Img image) {
this.image = image;}
public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + image + ")"; }public Object[] _fieldsToList() { return new Object[] {image}; }
int w, h, runner;
final public int getSize(){ return size(); }
public int size() { return size; }
int size; // =w*h
transient Set> onTileStarted;
public G22AbstractTiler onTileStarted(IVF1 f) { onTileStarted = createOrAddToSyncLinkedHashSet(onTileStarted, f); return this; }
public G22AbstractTiler removeTileStartedListener(IVF1 f) { main.remove(onTileStarted, f); return this; }
public void tileStarted(GrowingTile tile) { if (onTileStarted != null) for (var listener : onTileStarted) pcallF_typed(listener, tile); }
transient Set> onTileDone;
public G22AbstractTiler onTileDone(IVF1 f) { onTileDone = createOrAddToSyncLinkedHashSet(onTileDone, f); return this; }
public G22AbstractTiler removeTileDoneListener(IVF1 f) { main.remove(onTileDone, f); return this; }
public void tileDone(GrowingTile tile) { if (onTileDone != null) for (var listener : onTileDone) pcallF_typed(listener, tile); }
// iterator that provides points to be looked at
// (e.g. a WeightlessShuffledIterator)
IndexIterator pointIterator;
// Temporary stack of points to be looked at
// (e.g. neighbors of a tile just discovered).
// May or may not be used.
IntBuffer stack = new IntBuffer();
G22Tiling tiling; // the tiling we are making
int tileCounter;
GrowingTile growingTile; // tile currently being grown
boolean verbose = false;
// The one method subclasses need to override.
// The exact meaning of a "color" is not defined,
// the tiler just treats different int value as different colors.
abstract int getColor(int pos);
// calculations for positions (pixel index)
int x(int pos) { return pos % w; }
int y(int pos) { return pos / w; }
int pos(int x, int y) { return y*w+x; }
Pt pt(int pos) { return new Pt(x(pos), y(pos)); }
boolean validPos(int x, int y) { return x >= 0 && y >= 0 && x < w && y < h; }
int getColor(int x, int y) { return getColor(pos(x, y)); }
class GrowingTile implements Steppable {
G22Tiling.Tile tile;
int color;
// Still growing in any of the 4 directions?
boolean growingN = true;
boolean growingE = true;
boolean growingS = true;
boolean growingW = true;
GrowingTile(int pos) {
int x = x(pos), y = y(pos);
color = getColor(x, y);
int iTile = tileCounter++;
tile = tiling.new Tile(iTile, color, rect(x, y, 1, 1));
tiling.tiles.add(tile);
}
// put tile into tileMatrix
void finish() {
Rect r = tile.position();
int rx = r.x, ry = r.y, rw = r.w, rh = r.h;
int[] matrix = tiling.tileMatrix;
int iTile = tile.index;
for (int y = 0; y < rh; y++)
for (int x = 0; x < rw; x++) {
int pos = (ry+y)*w+(rx+x);
if (matrix[pos] != 0)
throw fail("Overlapping tiles!");
matrix[pos] = iTile+1;
}
tiling.pixelsCovered += rw*rh;
}
public boolean step() {
Rect r = nextRect();
if (r == null) {
finish();
return false;
}
tile.position(r);
return true;
}
// Is this pixel in the image and the tile's color
// and not in a tile?
boolean isContinuation(int x, int y) {
return validPos(x, y) && isContinuation_impl(x, y);
}
boolean isContinuation_impl(int x, int y) {
return tiling.tileMatrix[pos(x, y)] == 0 && getColor(x, y) == color;
}
// Is this rectangle completely in the image and the tile's color
// and not in a tile?
boolean isContinuation(int x1, int y1, int w, int h) {
if (!validPos(x1, y1) || !validPos(x1+w-1, y1+h-1)) return false;
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
if (!isContinuation_impl(x1+x, y1+y))
return false;
return true;
}
Rect nextRect() {
Rect r = tile.position();
// Check the 4 sides for possible growth
growingN = growingN && isContinuation(r.x, r.y-1, r.w, 1);
growingS = growingS && isContinuation(r.x, r.y2(), r.w, 1);
growingW = growingW && isContinuation(r.x-1, r.y, 1, r.h);
growingE = growingE && isContinuation(r.x2(), r.y, 1, r.h);
// Check the corners too (including the adjacent sides)
boolean cornerNW = growingN && growingW && isContinuation(r.x-1, r.y-1);
boolean cornerNE = growingN && growingE && isContinuation(r.x2(), r.y-1);
boolean cornerSW = growingS && growingW && isContinuation(r.x-1, r.y2());
boolean cornerSE = growingS && growingE && isContinuation(r.x2(), r.y2());
// Corners are the best case, so try those first
if (cornerNW) {
int x2 = r.x2(), y2 = r.y2(); // Default case: Grow to NE only
if (cornerSW && cornerNE && cornerSE) {
// Best case - grow in all 4 directions
x2++; y2++;
} else if (cornerNE)
// At least grow to the right too
x2++;
else if (cornerSW)
// At least grow to the south too
y2++;
return rectFromPoints(r.x-1, r.y-1, x2, y2);
} else if (cornerNE) {
// Can't grow NW but will grow NE. So only south growth to decide
int y2 = r.y2();
if (cornerSE)
// Grow south too
y2++;
return rectFromPoints(r.x, r.y-1, r.x2()+1, y2);
} else if (cornerSW) {
// Can't grow NW or NE but will grow SW. So only east growth to decide
int x2 = r.x2();
if (cornerSE)
// Grow east too
x2++;
return rectFromPoints(r.x-1, r.y, x2, r.y2()+1);
} else if (cornerSE) {
// Only growable corner is SE, so just do it
return rectFromPoints(r.x, r.y, r.x2()+1, r.y2()+1);
} else {
// No growable corners. Try growing west/east or north/south
if (growingW || growingE)
return rectFromPoints(r.x-(growingW ? 1 : 0), r.y,
r.x2()+(growingE ? 1 : 0), r.y2());
else if (growingN || growingS)
return rectFromPoints(r.x, r.y-(growingN ? 1 : 0),
r.x2(), r.y2()+(growingS ? 1 : 0));
}
// No more growth possible
return null;
}
}
public void run() { try { stepAll(this); } catch (Exception __e) { throw rethrow(__e); } }
public boolean step() {
init();
// Currently growing a tile? Continue that
if (growingTile != null) {
if (growingTile.step()) return true;
tileDone(growingTile);
growingTile = null; // Done growing this tile
}
int pos = pointIterator.nextIndex();
if (pos < 0) return false;
// Is point already covered by a tile?
if (tiling.tileMatrix[pos] != 0) return true;
// Create new tile
growingTile = new GrowingTile(pos);
tileStarted(growingTile);
return true;
}
void init() {
if (tiling != null) return;
w = image.getWidth(); h = image.getHeight();
size = w*h;
tiling = new G22Tiling(image);
tiling.initTileMatrix();
pointIterator = new WeightlessShuffledIterator(size);
}
G22Tiling get() {
if (tiling == null) run();
return tiling;
}
}
static String shortClassName_dropNumberPrefix(Object o) {
return dropNumberPrefix(shortClassName(o));
}
static Set createOrAddToSyncLinkedHashSet(Set set, A a) {
if (set == null) set = syncLinkedHashSet();
set.add(a);
return set;
}
static void remove(List l, int i) {
if (l != null && i >= 0 && i < l(l))
l.remove(i);
}
static void remove(Collection l, A a) {
if (l != null) l.remove(a);
}
static B remove(Map map, Object a) {
return map == null ? null : map.remove(a);
}
static void remove(BitSet bs, int i) {
bs.clear(i);
}
static A pcallF_typed(F0 f) {
try { return f == null ? null : f.get(); } catch (Throwable __e) { printStackTrace(__e); } return null;
}
static B pcallF_typed(F1 f, A a) {
try { return f == null ? null : f.get(a); } catch (Throwable __e) { printStackTrace(__e); } return null;
}
static void pcallF_typed(VF1 f, A a) {
try {
{ if (f != null) f.get(a); }
} catch (Throwable __e) { printStackTrace(__e); }
}
static void pcallF_typed(IVF1 f, A a) {
try {
{ if (f != null) f.get(a); }
} catch (Throwable __e) { printStackTrace(__e); }
}
static void pcallF_typed(IVF2 f, A a, B b) {
try {
{ if (f != null) f.get(a, b); }
} catch (Throwable __e) { printStackTrace(__e); }
}
static Object pcallF_typed(Runnable r) {
try { { if (r != null) r.run(); } } catch (Throwable __e) { printStackTrace(__e); } return null;
}
static A pcallF_typed(IF0 f) {
try { return f == null ? null : f.get(); } catch (Throwable __e) { printStackTrace(__e); } return null;
}
static B pcallF_typed(IF1 f, A a) {
try { return f == null ? null : f.get(a); } catch (Throwable __e) { printStackTrace(__e); } return null;
}
static Color getColor(BufferedImage img, int x, int y) {
return colorFromRGBA(img.getRGB(x, y));
}
static Color getColor(BufferedImage img, Pt p) {
return colorFromRGBA(img.getRGB(p.x, p.y));
}
static Rect rect(int x, int y, int w, int h) {
return new Rect(x, y, w, h);
}
static Rect rect(Pt p, int w, int h) {
return new Rect(p.x, p.y, w, h);
}
static Rect rect(int w, int h) {
return new Rect(0, 0, w, h);
}
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 Rect rectFromPoints(int x1, int y1, int x2, int y2) {
return pointsRect(x1, y1, x2, y2);
}
static Rect rectFromPoints(Pt a, Pt b) {
return pointsRect(a.x, a.y, b.x, b.y);
}
// returns number of steps
static long stepAll(Steppable s) {
long steps = 0;
if (s != null) {
var pingSource = pingSource();
while (true) {
ping(pingSource);
if (s.step())
++steps;
else
break;
}
}
return steps;
}
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);
}
// get purpose 1: access a list/array/map (safer version of x.get(y))
static A get(List l, int idx) {
return l != null && idx >= 0 && idx < l(l) ? l.get(idx) : null;
}
// seems to conflict with other signatures
/*static B get(Map map, A key) {
ret map != null ? map.get(key) : null;
}*/
static A get(A[] l, int idx) {
return idx >= 0 && idx < l(l) ? l[idx] : null;
}
// default to false
static boolean get(boolean[] l, int idx) {
return idx >= 0 && idx < l(l) ? l[idx] : false;
}
// get purpose 2: access a field by reflection or a map
static Object get(Object o, String field) {
try {
if (o == null) return null;
if (o instanceof Class) return get((Class) o, field);
if (o instanceof Map)
return ((Map) o).get(field);
Field f = getOpt_findField(o.getClass(), field);
if (f != null) {
makeAccessible(f);
return f.get(o);
}
if (o instanceof DynamicObject)
return getOptDynOnly(((DynamicObject) o), field);
} catch (Exception e) {
throw asRuntimeException(e);
}
throw new RuntimeException("Field '" + field + "' not found in " + o.getClass().getName());
}
static Object get_raw(String field, Object o) {
return get_raw(o, field);
}
static Object get_raw(Object o, String field) { try {
if (o == null) return null;
Field f = get_findField(o.getClass(), field);
makeAccessible(f);
return f.get(o);
} catch (Exception __e) { throw rethrow(__e); } }
static Object get(Class c, String field) {
try {
Field f = get_findStaticField(c, field);
makeAccessible(f);
return f.get(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static Field get_findStaticField(Class> c, String field) {
Class _c = c;
do {
for (Field f : _c.getDeclaredFields())
if (f.getName().equals(field) && (f.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0)
return f;
_c = _c.getSuperclass();
} while (_c != null);
throw new RuntimeException("Static field '" + field + "' not found in " + c.getName());
}
static Field get_findField(Class> c, String field) {
Class _c = c;
do {
for (Field f : _c.getDeclaredFields())
if (f.getName().equals(field))
return f;
_c = _c.getSuperclass();
} while (_c != null);
throw new RuntimeException("Field '" + field + "' not found in " + c.getName());
}
static Object get(String field, Object o) {
return get(o, field);
}
static boolean get(BitSet bs, int idx) {
return bs != null && bs.get(idx);
}
static Class run(String progID, String... args) {
Class main = hotwire(progID);
callMain(main, args);
return main;
}
static String dropNumberPrefix(String s) {
return dropFirst(s, indexOfNonDigit(s));
}
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);
}
static Set syncLinkedHashSet() {
return synchroLinkedHashSet();
}
static int l(Object[] a) { return a == null ? 0 : a.length; }
static int l(boolean[] a) { return a == null ? 0 : a.length; }
static int l(byte[] a) { return a == null ? 0 : a.length; }
static int l(short[] a) { return a == null ? 0 : a.length; }
static int l(long[] a) { return a == null ? 0 : a.length; }
static int l(int[] a) { return a == null ? 0 : a.length; }
static int l(float[] a) { return a == null ? 0 : a.length; }
static int l(double[] a) { return a == null ? 0 : a.length; }
static int l(char[] a) { return a == null ? 0 : a.length; }
static int l(Collection c) { return c == null ? 0 : c.size(); }
static int l(Iterator i) { return iteratorCount_int_close(i); } // consumes the iterator && closes it if possible
static int l(Map m) { return m == null ? 0 : m.size(); }
static int l(CharSequence s) { return s == null ? 0 : s.length(); }
static long l(File f) { return f == null ? 0 : f.length(); }
static int l(IMultiMap mm) { return mm == null ? 0 : mm.size(); }
static int l(IntBuffer b) { return b == null ? 0 : b.size(); }
static int l(AppendableChain a) { return a == null ? 0 : a.size; }
static A printStackTrace(A e) {
// we go to system.out now - system.err is nonsense
if (e != null) 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 Color colorFromRGBA(int rgba) {
return new Color(rgba, true);
}
static RuntimeException asRuntimeException(Throwable t) {
if (t instanceof Error)
_handleError((Error) t);
return t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t);
}
static Rect pointsRect(int x1, int y1, int x2, int y2) {
return new Rect(x1, y1, x2-x1, y2-y1);
}
// assumptions:
// -pingSource() stays constant within a stack frame (so you can cache it)
// -all changes happen with tempSetPingSource
// -multiple threads can share a PingSource
static PingSource pingSource() {
return pingSource_tl().get();
}
static PingSource pingSource(Thread thread) {
return pingSource_tl().get(thread);
}
// legacy mode
//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();
// ignore pingSource if not PingV3
static boolean ping(PingSource pingSource) { return ping(); }
// always returns true
static boolean ping() {
//ifdef useNewPing
newPing();
//endifdef
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 void _handleError(Error e) {
//call(javax(), '_handleError, e);
}
static Field getOpt_findField(Class> c, String field) {
Class _c = c;
do {
for (Field f : _c.getDeclaredFields())
if (f.getName().equals(field))
return f;
_c = _c.getSuperclass();
} while (_c != null);
return null;
}
static Field makeAccessible(Field f) {
try {
f.setAccessible(true);
} catch (Throwable e) {
// Note: The error reporting only works with Java VM option --illegal-access=deny
vmBus_send("makeAccessible_error", e, f);
}
return f;
}
static Method makeAccessible(Method m) {
try {
m.setAccessible(true);
} catch (Throwable e) {
vmBus_send("makeAccessible_error", e, m);
}
return m;
}
static Constructor makeAccessible(Constructor c) {
try {
c.setAccessible(true);
} catch (Throwable e) {
vmBus_send("makeAccessible_error", e, c);
}
return c;
}
static Object getOptDynOnly(DynamicObject o, String field) {
if (o == null || o.fieldValues == null) return null;
return o.fieldValues.get(field);
}
// custom mainClass only works with hotwire_here
static Class> hotwire(String src) { return hotwire(src, __1 -> mainClassNameForClassLoader(__1)); }
static Class> hotwire(String src, IF1 calculateMainClass) {
assertFalse(_inCore());
Class j = getJavaX();
if (isAndroid()) {
synchronized(j) { // hopefully this goes well...
List libraries = new ArrayList();
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;
}
}
static A callMain(A c, String... args) {
callOpt(c, "main", new Object[] {args});
return c;
}
static void callMain() {
callMain(mc());
}
static String[] dropFirst(int n, String[] a) {
return drop(n, a);
}
static String[] dropFirst(String[] a) {
return drop(1, a);
}
static Object[] dropFirst(Object[] a) {
return drop(1, a);
}
static List dropFirst(List l) {
return dropFirst(1, l);
}
static List dropFirst(int n, Iterable i) { return dropFirst(n, toList(i)); }
static List dropFirst(Iterable i) { return dropFirst(toList(i)); }
static List dropFirst(int n, List l) {
return n <= 0 ? l : new ArrayList(l.subList(Math.min(n, l.size()), l.size()));
}
static List dropFirst(List l, int n) {
return dropFirst(n, l);
}
static String dropFirst(int n, String s) { return substring(s, n); }
static String dropFirst(String s, int n) { return substring(s, n); }
static String dropFirst(String s) { return substring(s, 1); }
static Chain dropFirst(Chain c) {
return c == null ? null : c.next;
}
static int indexOfNonDigit(String s) {
int n = l(s);
for (int i = 0; i < n; i++)
if (!isDigit(s.charAt(i)))
return i;
return -1;
}
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);
}
// BREAKING CHANGE!
// Also NOTE: Iterators of these sync-wrapped collections
// after generally NOT thread-safe!
// TODO: change that?
static Set synchroLinkedHashSet() {
return synchronizedSet(new CompactLinkedHashSet());
}
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 volatile StringBuffer local_log = new StringBuffer(); // not redirected
static boolean printAlsoToSystemOut = true;
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